From eed258505ee1ad0f72d9e0a8a3934f2e9e7b5e79 Mon Sep 17 00:00:00 2001 From: Hyeongseok Oh Date: Fri, 8 Sep 2023 10:51:25 +0000 Subject: [PATCH] Imported Upstream version 1.25.0 --- .ahub/sam/exclude.txt | 3 + .ahub/tcchecker-tca/config.yaml | 88 +- .github/workflows/run-onert-micro-unit-tests.yml | 47 + Makefile.template | 32 +- compiler/CMakeLists.txt | 4 +- compiler/arser/CMakeLists.txt | 2 +- compiler/circle-eval-diff/src/InputDataLoader.cpp | 8 +- compiler/circle-inspect/CMakeLists.txt | 8 +- compiler/circle-inspect/requires.cmake | 2 +- compiler/circle-interpreter-test/CMakeLists.txt | 18 +- compiler/circle-interpreter-test/requires.cmake | 1 + compiler/circle-mpqsolver/CMakeLists.txt | 19 +- compiler/circle-mpqsolver/README.md | 4 + compiler/circle-mpqsolver/src/CircleMPQSolver.cpp | 63 +- compiler/circle-mpqsolver/src/MPQSolver.cpp | 5 + compiler/circle-mpqsolver/src/MPQSolver.h | 8 +- .../src/bisection/BisectionSolver.cpp | 102 +- .../src/bisection/BisectionSolver.h | 18 +- .../src/bisection/DepthParameterizer.test.cpp | 2 +- .../src/bisection/ErrorApproximator.cpp | 387 - .../src/bisection/ErrorApproximator.test.cpp | 136 - .../src/bisection/VISQErrorApproximator.cpp | 29 +- .../src/bisection/VISQErrorApproximator.h | 4 +- .../src/bisection/VISQErrorApproximator.test.cpp | 83 + compiler/circle-mpqsolver/src/core/Dumper.cpp | 160 + compiler/circle-mpqsolver/src/core/Dumper.h | 113 + .../circle-mpqsolver/src/core/DumpingHooks.cpp | 62 + compiler/circle-mpqsolver/src/core/DumpingHooks.h | 84 + .../src/{bisection => core}/ErrorMetric.cpp | 7 +- .../src/{bisection => core}/ErrorMetric.h | 10 +- .../circle-mpqsolver/src/core/ErrorMetric.test.cpp | 56 + .../src/{bisection => core}/Evaluator.cpp | 7 +- .../src/{bisection => core}/Evaluator.h | 10 +- .../src/{bisection => core}/Quantizer.cpp | 9 +- .../src/{bisection => core}/Quantizer.h | 25 +- .../src/{bisection => core}/Quantizer.test.cpp | 8 +- .../circle-mpqsolver/src/core/SolverHooks.cpp | 10 +- compiler/circle-mpqsolver/src/core/SolverHooks.h | 69 + .../circle-mpqsolver/src/core/SolverOutput.cpp | 47 + compiler/circle-mpqsolver/src/core/SolverOutput.h | 58 + .../src/{bisection => core}/TestHelper.h | 0 compiler/circle-operator/CMakeLists.txt | 8 +- compiler/circle-operator/requires.cmake | 2 +- compiler/circle-opselector/driver/Driver.cpp | 6 +- compiler/circle-opselector/src/OpSelector.cpp | 19 +- compiler/circle-part-value-test/CMakeLists.txt | 12 +- .../CMakeLists.txt | 19 + .../circle-quantizer-dredd-recipe-test/test.lst | 12 + compiler/circle-quantizer/src/CircleQuantizer.cpp | 27 +- compiler/circle-tensordump/CMakeLists.txt | 8 +- compiler/circle-tensordump/requires.cmake | 2 +- compiler/circle-tensordump/src/Dump.cpp | 4 + compiler/circle-verify/CMakeLists.txt | 8 +- compiler/circle-verify/requires.cmake | 2 +- compiler/circle2circle-dredd-recipe-test/test.lst | 6 + compiler/circle2circle/src/Circle2Circle.cpp | 42 + compiler/circlechef/CMakeLists.txt | 6 +- compiler/circlechef/circle/CMakeLists.txt | 4 +- compiler/circlechef/core/CMakeLists.txt | 2 +- compiler/circlechef/requires.cmake | 2 +- compiler/circledump/CMakeLists.txt | 10 +- compiler/circledump/README.md | 2 +- compiler/circledump/requires.cmake | 2 +- compiler/circledump/src/MetadataPrinter.h | 1 + compiler/circledump/src/OpPrinter.cpp | 27 +- compiler/common-artifacts/CMakeLists.txt | 91 +- compiler/common-artifacts/exclude.lst | 12 + compiler/dalgona-test/CMakeLists.txt | 13 +- compiler/dalgona/src/Dalgona.cpp | 5 +- compiler/dalgona/src/PostOperatorHook.h | 20 +- compiler/dalgona/src/Utils.cpp | 22 + compiler/dalgona/src/Utils.h | 2 + compiler/dio-hdf5/include/dio_hdf5/HDF5Importer.h | 5 +- compiler/dio-hdf5/src/HDF5Importer.cpp | 11 +- compiler/dio-hdf5/src/HDF5Importer.test.cpp | 23 +- compiler/loco/CMakeLists.txt | 2 +- compiler/luci-eval-driver/src/EvalDriver.cpp | 7 +- .../include/luci_interpreter/core/Tensor.h | 4 +- .../luci-interpreter/pal/linux/KernelsToBuild.lst | 6 + .../luci-interpreter/pal/linux/PALGelu.h | 18 +- .../luci-interpreter/pal/linux/PALHardSwish.h | 14 +- compiler/luci-interpreter/pal/linux/pal.cmake | 63 +- compiler/luci-interpreter/src/core/KernelParams.h | 6 + compiler/luci-interpreter/src/kernels/FloorMod.cpp | 132 + .../luci-interpreter/src/kernels/FloorMod.h | 20 +- .../luci-interpreter/src/kernels/FloorMod.test.cpp | 446 + compiler/luci-interpreter/src/kernels/Gelu.cpp | 63 + .../luci-interpreter/src/kernels/Gelu.h | 16 +- .../luci-interpreter/src/kernels/Gelu.test.cpp | 115 + .../luci-interpreter/src/kernels/HardSwish.cpp | 52 + .../luci-interpreter/src/kernels/HardSwish.h | 13 +- .../src/kernels/HardSwish.test.cpp | 81 + compiler/luci-interpreter/src/kernels/Log.cpp | 56 + .../luci-interpreter/src/kernels/Log.h | 14 +- compiler/luci-interpreter/src/kernels/Log.test.cpp | 71 + compiler/luci-interpreter/src/kernels/Reshape.cpp | 24 +- .../luci-interpreter/src/kernels/Reshape.test.cpp | 36 + compiler/luci-interpreter/src/kernels/Select.cpp | 100 + .../luci-interpreter/src/kernels/Select.h | 30 +- .../luci-interpreter/src/kernels/Select.test.cpp | 106 + compiler/luci-interpreter/src/kernels/Sub.cpp | 2 +- compiler/luci-interpreter/src/kernels/Sum.cpp | 179 + .../luci-interpreter/src/kernels/Sum.h | 20 +- compiler/luci-interpreter/src/kernels/Sum.test.cpp | 145 + .../src/kernels/TransposeConv.test.cpp | 5 + .../src/loader/KernelBuilder.test.cpp | 2 + .../luci-interpreter/src/loader/nodes/FloorMod.cpp | 19 +- .../luci-interpreter/src/loader/nodes/Gelu.cpp | 21 +- .../src/loader/nodes/HardSwish.cpp | 21 +- .../luci-interpreter/src/loader/nodes/Log.cpp | 18 +- .../luci-interpreter/src/loader/nodes/Select.cpp | 20 +- compiler/luci-interpreter/src/loader/nodes/Sum.cpp | 54 + .../src/loader/nodes/TransposeConv.cpp | 7 + compiler/luci-pass-value-test/CMakeLists.txt | 13 +- compiler/luci-pass-value-test/test.lst | 1 + compiler/luci-value-test/CMakeLists.txt | 26 +- compiler/luci-value-test/test.lst | 23 +- compiler/luci/export/CMakeLists.txt | 4 +- .../luci/export/src/CircleBuiltinTypesExtractor.h | 8 +- compiler/luci/export/src/CircleOps.lst | 2 + compiler/luci/import/CMakeLists.txt | 4 +- .../luci/import/include/luci/Import/NodeBuilder.h | 1 + compiler/luci/import/include/luci/Import/Nodes.h | 2 + .../import/include/luci/Import/Nodes/CircleGelu.h | 37 + .../include/luci/Import/Nodes/CircleHardSwish.h | 37 + compiler/luci/import/src/GraphBuilderRegistry.cpp | 13 +- compiler/luci/import/src/Nodes/CircleGelu.cpp | 44 + compiler/luci/import/src/Nodes/CircleHardSwish.cpp | 41 + compiler/luci/import/src/Nodes/CircleReshape.cpp | 7 +- .../luci/import/src/Nodes/CircleTransposeConv.cpp | 1 + compiler/luci/lang/include/luci/IR/CircleNodes.h | 2 + compiler/luci/lang/include/luci/IR/CircleNodes.lst | 2 + .../luci/lang/include/luci/IR/Nodes/CircleGelu.h | 47 + .../lang/include/luci/IR/Nodes/CircleHardSwish.h | 40 + .../include/luci/IR/Nodes/CircleTransposeConv.h | 1 + compiler/luci/lang/src/Nodes/CircleGelu.test.cpp | 81 + .../luci/lang/src/Nodes/CircleHardSwish.test.cpp | 76 + .../luci/logex/src/CircleNodeSummaryBuilder.cpp | 18 +- .../logex/src/CircleNodeSummaryBuilder.test.cpp | 8 + .../luci/logex/src/CircleNodeSummaryBuilders.cpp | 9 + .../luci/logex/src/CircleNodeSummaryBuilders.h | 10 + compiler/luci/partition/CMakeLists.txt | 2 +- compiler/luci/partition/include/luci/ConnectNode.h | 2 + .../luci/partition/src/Nodes/CircleGelu.cpp | 28 +- .../luci/partition/src/Nodes/CircleGelu.test.cpp | 90 + .../luci/partition/src/Nodes/CircleHardSwish.cpp | 38 + .../partition/src/Nodes/CircleHardSwish.test.cpp | 90 + .../src/Nodes/CircleTransposeConv.test.cpp | 1 + compiler/luci/pass/include/luci/CircleOptimizer.h | 2 + compiler/luci/pass/include/luci/CircleQuantizer.h | 1 + .../pass/include/luci/DynamicBatchToSingleBatch.h | 29 + .../include/luci/Pass/DecomposeHardSwishPass.h | 27 +- .../luci/Pass/DynamicBatchToSingleBatchPass.h | 39 + .../luci/pass/include/luci/Pass/FuseGeluPass.h | 39 + .../pass/include/luci/Pass/QuantizeWeightsPass.h | 70 + .../luci/pass/include/luci/Pass/RequantizePass.h | 2 +- compiler/luci/pass/src/CircleOptimizer.cpp | 42 +- compiler/luci/pass/src/CircleQuantizer.cpp | 39 +- compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp | 26 +- .../luci/pass/src/ConvertNCHWToNHWCPass.test.cpp | 8 +- .../pass/src/ConvertToFakeQuantizedModelPass.cpp | 4 + compiler/luci/pass/src/DecomposeHardSwishPass.cpp | 147 + .../luci/pass/src/DecomposeHardSwishPass.test.cpp | 205 + .../luci/pass/src/DynamicBatchToSingleBatch.cpp | 51 + .../pass/src/DynamicBatchToSingleBatchPass.cpp | 78 + .../src/DynamicBatchToSingleBatchPass.test.cpp | 126 + compiler/luci/pass/src/FoldAddV2Pass.test.cpp | 8 +- compiler/luci/pass/src/FoldCastPass.test.cpp | 4 +- compiler/luci/pass/src/FoldDequantizePass.test.cpp | 4 +- .../luci/pass/src/FuseActivationFunctionPass.cpp | 5 + .../src/FuseAddWithFullyConnectedPass.test.cpp | 54 +- compiler/luci/pass/src/FuseAddWithTConvPass.cpp | 3 + .../luci/pass/src/FuseBatchNormWithTConvPass.cpp | 6 + compiler/luci/pass/src/FuseGeluPass.cpp | 347 + compiler/luci/pass/src/FuseGeluPass.test.cpp | 251 + .../luci/pass/src/PropagateQParamBackwardPass.cpp | 63 + .../pass/src/PropagateQParamBackwardPass.test.cpp | 143 + compiler/luci/pass/src/QuantizationUtils.cpp | 19 +- compiler/luci/pass/src/QuantizationUtils.h | 8 +- compiler/luci/pass/src/QuantizeActivation.cpp | 10 +- compiler/luci/pass/src/QuantizeActivation.h | 3 + compiler/luci/pass/src/QuantizeBias.test.cpp | 47 +- .../pass/src/QuantizeDequantizeWeightsPass.cpp | 8 +- .../luci/pass/src/QuantizePreCheckerPass.test.cpp | 1 + compiler/luci/pass/src/QuantizeWeights.cpp | 9 +- compiler/luci/pass/src/QuantizeWeightsOnly.cpp | 224 + compiler/luci/pass/src/QuantizeWeightsOnly.h | 51 + compiler/luci/pass/src/QuantizeWeightsPass.cpp | 46 + .../luci/pass/src/QuantizeWeightsPass.test.cpp | 123 + compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp | 16 +- .../luci/pass/src/QuantizedModelVerifier.test.cpp | 73 +- .../ReplaceNonConstFCWithBatchMatMulPass.test.cpp | 52 +- compiler/luci/pass/src/ReplaceSubWithAddPass.cpp | 2 + compiler/luci/pass/src/RequantizePass.cpp | 159 +- compiler/luci/pass/src/RequantizePass.test.cpp | 156 + .../luci/pass/src/ResolveCustomOpMatMulPass.cpp | 58 +- .../pass/src/SubstituteSplitVToSplitPass.test.cpp | 51 +- .../luci/pass/src/VerifyQuantizedBiasScale.cpp | 2 +- .../luci/pass/src/VerifyQuantizedNodeGranularity.h | 14 + compiler/luci/pass/src/VerifyQuantizedNodeType.cpp | 15 + compiler/luci/pass/src/VerifyQuantizedNodeType.h | 2 + .../luci/pass/src/helpers/CreateCircleConst.cpp | 11 +- compiler/luci/pass/src/helpers/CreateCircleConst.h | 88 + compiler/luci/pass/src/helpers/TypeMapper.h | 5 + compiler/luci/requires.cmake | 2 +- .../include/luci/Service/CircleShapeInference.h | 1 + .../include/luci/Service/CircleTypeInference.h | 1 + .../luci/service/include/luci/Service/Validate.h | 4 + compiler/luci/service/src/CircleCloneNode.h | 2 + .../luci/service/src/CircleShapeInferenceRule.cpp | 20 +- .../luci/service/src/CircleTypeInferenceRule.cpp | 10 + compiler/luci/service/src/Nodes/CircleGelu.cpp | 30 + .../luci/service/src/Nodes/CircleGelu.test.cpp | 35 + .../luci/service/src/Nodes/CircleHardSwish.cpp | 27 + .../service/src/Nodes/CircleHardSwish.test.cpp | 74 + .../luci/service/src/Nodes/CircleTransposeConv.cpp | 1 + .../service/src/Nodes/CircleTransposeConv.test.cpp | 12 + .../luci/service/src/ShapeInfer_StridedSlice.cpp | 97 +- compiler/luci/service/src/Validate.cpp | 92 + compiler/luci/tests/test.lst | 4 + compiler/mio-circle05/CMakeLists.txt | 52 + compiler/mio-circle05/README.md | 3 + compiler/mio-circle05/example.cpp | 41 + compiler/mio-circle05/include/mio_circle/Helper.h | 54 + compiler/mio-circle05/include/mio_circle/Reader.h | 101 + compiler/mio-circle05/src/Helper.cpp | 110 + compiler/mio-circle05/src/Helper.test.cpp | 153 + compiler/mio-circle05/src/Reader.cpp | 147 + compiler/mio-circle05/src/Reader.test.cpp | 60 + compiler/mio-circle06/CMakeLists.txt | 52 + compiler/mio-circle06/README.md | 3 + compiler/mio-circle06/example.cpp | 41 + compiler/mio-circle06/include/mio_circle/Helper.h | 54 + compiler/mio-circle06/include/mio_circle/Reader.h | 101 + compiler/mio-circle06/src/Helper.cpp | 110 + compiler/mio-circle06/src/Helper.test.cpp | 153 + compiler/mio-circle06/src/Reader.cpp | 147 + compiler/mio-circle06/src/Reader.test.cpp | 60 + compiler/mio-tflite2121/CMakeLists.txt | 60 + compiler/mio-tflite2121/README.md | 3 + compiler/mio-tflite2121/example.cpp | 41 + .../mio-tflite2121/include/mio_tflite2121/Helper.h | 37 + compiler/mio-tflite2121/src/Helper.cpp | 104 + compiler/mio-tflite2121/src/Helper.test.cpp | 159 + compiler/mir/unittests/ShapeRange.cpp | 2 +- .../src/Canonicalization/SoftmaxCanonicalizer.h | 2 +- .../code_snippets/cpp_header_types.def | 2 +- .../backends/soft_backend/code_snippets/eigen.def | 2 +- compiler/one-cmds/CMakeLists.txt | 21 +- compiler/one-cmds/dummy-driver/CMakeLists.txt | 36 + .../dummy-driver/src/dummy-onnx-ext.cpp} | 33 +- .../one-cmds/dummy-driver/src/dummyV2-compile.cpp | 51 + .../one-cmds/dummy-driver/src/dummyV2-profile.cpp | 36 + .../one-cmds/dummy-driver/src/dummyV3-profile.cpp | 38 + compiler/one-cmds/one-codegen | 175 +- compiler/one-cmds/one-import-onnx | 182 +- compiler/one-cmds/one-import-pytorch | 3 +- compiler/one-cmds/one-infer | 39 +- compiler/one-cmds/one-init | 42 +- compiler/one-cmds/one-prepare-venv | 22 +- compiler/one-cmds/one-prepare-venv.aarch64 | 139 + ...e-prepare-venv.u2204 => one-prepare-venv.u1804} | 19 +- compiler/one-cmds/one-profile | 120 +- compiler/one-cmds/one-quantize | 304 + compiler/one-cmds/onecc | 57 +- compiler/one-cmds/onelib/CfgRunner.py | 7 + compiler/one-cmds/onelib/backends.py | 79 + compiler/one-cmds/onelib/constant.py | 7 +- compiler/one-cmds/onelib/utils.py | 26 +- compiler/one-cmds/requires.cmake | 1 + compiler/one-cmds/tests/one-codegen_neg_001.test | 2 +- compiler/one-cmds/tests/one-codegen_neg_002.test | 41 + compiler/one-cmds/tests/one-codegen_neg_003.test | 41 + compiler/one-cmds/tests/one-codegen_neg_004.cfg | 6 + compiler/one-cmds/tests/one-codegen_neg_004.test | 43 + compiler/one-cmds/tests/one-codegen_neg_005.test | 41 + .../one-cmds/tests/one-import-onnx_ext_001.test | 53 + compiler/one-cmds/tests/one-import_neg_001.test | 5 + compiler/one-cmds/tests/one-profile_neg_002.test | 41 + compiler/one-cmds/tests/one-profile_neg_003.test | 41 + compiler/one-cmds/tests/one-profile_neg_004.cfg | 6 + compiler/one-cmds/tests/one-profile_neg_004.test | 43 + compiler/one-cmds/tests/one-profile_neg_005.test | 41 + compiler/one-cmds/tests/one-quantize_016.test | 4 +- compiler/one-cmds/tests/one-quantize_017.test | 46 + compiler/one-cmds/tests/one-quantize_018.test | 51 + compiler/one-cmds/tests/one-quantize_019.test | 51 + compiler/one-cmds/tests/one-quantize_020.test | 51 + compiler/one-cmds/tests/one-quantize_021.test | 47 + compiler/one-cmds/tests/one-quantize_neg_003.test | 2 +- compiler/one-cmds/tests/one-quantize_neg_022.test | 48 + compiler/one-cmds/tests/one-quantize_neg_023.test | 49 + compiler/one-cmds/tests/onecc_045.test | 4 +- compiler/one-cmds/tests/onecc_046.cfg | 7 + compiler/one-cmds/tests/onecc_046.test | 58 + compiler/one-cmds/tests/onecc_047.cfg | 7 + compiler/one-cmds/tests/onecc_047.test | 59 + compiler/one-cmds/tests/onecc_048.cfg | 6 + compiler/one-cmds/tests/onecc_048.test | 49 + compiler/one-cmds/tests/onecc_049.cfg | 7 + compiler/one-cmds/tests/onecc_049.test | 54 + compiler/one-cmds/tests/onecc_050.cfg | 7 + compiler/one-cmds/tests/onecc_050.test | 54 + compiler/one-cmds/tests/onecc_051.cfg | 6 + compiler/one-cmds/tests/onecc_051.test | 48 + compiler/one-cmds/tests/onecc_052.cfg | 13 + compiler/one-cmds/tests/onecc_052.test | 74 + compiler/one-cmds/tests/onecc_053.cfg | 13 + compiler/one-cmds/tests/onecc_053.test | 74 + compiler/one-cmds/tests/onecc_054.cfg | 7 + compiler/one-cmds/tests/onecc_054.test | 65 + compiler/one-cmds/tests/onecc_055.cfg | 6 + compiler/one-cmds/tests/onecc_055.test | 57 + compiler/one-cmds/tests/onecc_056.cfg | 6 + compiler/one-cmds/tests/onecc_056.test | 48 + compiler/one-cmds/tests/onecc_057.cfg | 7 + compiler/one-cmds/tests/onecc_057.test | 51 + compiler/one-cmds/tests/onecc_058.cfg | 9 + compiler/one-cmds/tests/onecc_058.test | 41 + compiler/one-cmds/tests/onecc_neg_027.cfg | 7 + compiler/one-cmds/tests/onecc_neg_027.test | 43 + compiler/one-cmds/tests/onecc_neg_028.cfg | 9 + compiler/one-cmds/tests/onecc_neg_028.test | 43 + compiler/one-cmds/tests/onecc_neg_029.cfg | 6 + compiler/one-cmds/tests/onecc_neg_029.test | 43 + compiler/one-cmds/tests/onecc_neg_030.cfg | 6 + compiler/one-cmds/tests/onecc_neg_030.test | 43 + compiler/one-cmds/tests/onecc_neg_031.test | 43 + .../one-cmds/tests/onecc_neg_031.workflow.json | 29 + compiler/one-cmds/tests/onecc_neg_032.cfg | 7 + compiler/one-cmds/tests/onecc_neg_032.test | 43 + compiler/one-cmds/tests/onecc_neg_033.cfg | 9 + compiler/one-cmds/tests/onecc_neg_033.test | 43 + compiler/one-cmds/tests/onecc_neg_034.cfg | 6 + compiler/one-cmds/tests/onecc_neg_034.test | 43 + compiler/one-cmds/tests/onecc_neg_035.cfg | 6 + compiler/one-cmds/tests/onecc_neg_035.test | 43 + compiler/one-cmds/tests/onecc_neg_036.test | 43 + .../one-cmds/tests/onecc_neg_036.workflow.json | 29 + compiler/one-cmds/tests/prepare_test_materials.sh | 18 +- compiler/one-cmds/tests/preprocess_images.py | 7 +- .../tests/pytorch-operations/example_generator.py | 3 +- compiler/onecc-docker/docker/Dockerfile | 4 +- compiler/onecc-docker/onecc-docker | 62 +- compiler/oops/requires.cmake | 1 + compiler/pics/CMakeLists.txt | 2 +- compiler/pics/requires.cmake | 1 + .../pota-quantization-value-test/CMakeLists.txt | 20 +- .../compare_tensors.py | 8 +- .../channel/int16/wo_quantization/ker.json | 61 + .../channel/int8/wo_quantization/ker.json | 61 + .../channel/int16/wo_quantization/ker.json | 53 + .../channel/int8/wo_quantization/ker.json | 53 + .../pota-quantization-value-test/requires.cmake | 1 - compiler/pota-quantization-value-test/test.lst | 5 + .../test_wo_quantization.sh | 87 + .../record-minmax-conversion-test/CMakeLists.txt | 13 +- .../CMakeLists.txt | 19 +- compiler/record-minmax/CMakeLists.txt | 21 +- compiler/record-minmax/driver/Driver.cpp | 45 +- compiler/record-minmax/include/MinMaxComputer.h | 85 + compiler/record-minmax/include/MinMaxObserver.h | 9 +- compiler/record-minmax/include/MinMaxVectors.h | 33 + compiler/record-minmax/include/RecordFunction.h | 77 +- compiler/record-minmax/include/RecordMinMax.h | 31 +- compiler/record-minmax/src/MinMaxComputer.cpp | 86 + compiler/record-minmax/src/RecordFunction.cpp | 97 + compiler/record-minmax/src/RecordMinMax.cpp | 76 +- .../record-minmax/tests/MinMaxComputer.test.cpp | 75 + compiler/souschef/include/souschef/Data/Gaussian.h | 21 + compiler/souschef/src/Gaussian.cpp | 18 + compiler/souschef/src/LexicalCast.cpp | 7 + .../tf2tfliteV2-conversion-test/CMakeLists.txt | 7 +- compiler/tfl-inspect/CMakeLists.txt | 8 +- compiler/tfl-inspect/requires.cmake | 2 +- compiler/tfl-inspect/src/Reader.cpp | 2 +- compiler/tfl-verify/CMakeLists.txt | 6 +- compiler/tfl-verify/requires.cmake | 2 +- compiler/tflchef/CMakeLists.txt | 6 +- compiler/tflchef/core/CMakeLists.txt | 2 +- compiler/tflchef/core/src/Convert.cpp | 2 + compiler/tflchef/core/src/CustomOp/Erf.cpp | 58 + compiler/tflchef/core/src/CustomOp/Erf.h | 49 + compiler/tflchef/core/src/DataChef.def | 3 + compiler/tflchef/core/src/ModelChef.cpp | 19 +- compiler/tflchef/core/src/Op/Gelu.cpp | 34 + compiler/tflchef/core/src/Op/Gelu.h | 46 + compiler/tflchef/core/src/Op/HardSwish.cpp | 27 + compiler/tflchef/core/src/Op/HardSwish.h | 46 + compiler/tflchef/core/src/Op/TransposeConv.cpp | 7 + compiler/tflchef/core/src/OpChef.def | 3 + compiler/tflchef/core/src/OpChefs.h | 3 + compiler/tflchef/proto/tflchef.proto | 7 + compiler/tflchef/requires.cmake | 2 +- compiler/tflchef/tests/CMakeLists.txt | 4 +- compiler/tflchef/tests/custom_erf/test.recipe | 17 + compiler/tflchef/tflite/CMakeLists.txt | 4 +- compiler/tflchef/tflite/src/Convert.cpp | 2 + compiler/tflchef/tflite/src/Op/Gelu.cpp | 46 + compiler/tflchef/tflite/src/Op/HardSwish.cpp | 40 + compiler/tflchef/tflite/src/Op/PRelu.cpp | 5 + compiler/tflchef/tflite/src/Op/TransposeConv.cpp | 2 + compiler/tflchef/tflite/src/Op/include/Gelu.h | 39 + compiler/tflchef/tflite/src/Op/include/HardSwish.h | 39 + compiler/tflchef/tflite/src/RecipeChef.cpp | 2 +- compiler/tflchef/tflite/src/TFliteImport.cpp | 2 +- compiler/tflchef/tflite/src/TFliteOpChefs.h | 2 + compiler/tflchef/tflite/src/TFliteOpRegistry.h | 2 + compiler/tfldump/CMakeLists.txt | 10 +- compiler/tfldump/requires.cmake | 2 +- compiler/tfldump/src/Dump.cpp | 2 +- compiler/tfldump/src/OpPrinter.cpp | 16 + compiler/tfldump/src/Read.cpp | 2 +- compiler/tflite2circle/CMakeLists.txt | 10 +- compiler/tflite2circle/requires.cmake | 4 +- compiler/tflite2circle/src/BuildBuiltinOptions.h | 1 + .../src/BuildBuiltinOptions/GeluOptions.cpp | 32 + .../src/BuildBuiltinOptions/GeluOptions.h | 31 + .../BuildBuiltinOptions/TransposeConvOptions.cpp | 2 + compiler/tflite2circle/src/CircleModel.cpp | 34 +- compiler/tflite2circle/src/TFLBuiltinOptions.lst | 1 + compiler/tflite2circle/src/TFLOperator.lst | 1 + compiler/vconone/CMakeLists.txt | 2 +- compiler/vconone/src/version.cpp | 2 +- compiler/visq-unittest/CMakeLists.txt | 10 +- compiler/visq-unittest/test/testDotBuilder.py | 1 - compiler/visq-unittest/test/testQErrorComputer.py | 97 +- compiler/visq/CMakeLists.txt | 2 +- compiler/visq/visq | 51 +- compiler/visq/visqlib/DumpFP32FM.py | 36 +- compiler/visq/visqlib/DumpFakeQuantFM.py | 44 +- compiler/visq/visqlib/QErrorComputer.py | 112 +- .../src/runtime/CL/functions/CLSplitVEx.cpp | 2 +- compute/cker/CMakeLists.txt | 4 + compute/cker/include/cker/PortableTensorUtils.h | 54 + compute/cker/include/cker/Shape.h | 2 +- compute/cker/include/cker/Types.h | 9 +- compute/cker/include/cker/eigen/eigen_gemm_eigen.h | 95 + compute/cker/include/cker/operation/Conv.h | 15 + .../cker/include/cker/operation/DepthwiseConv.h | 1 + compute/cker/include/cker/operation/Einsum.h | 6 +- .../cker/include/cker/operation/FullyConnected.h | 39 + .../cker/include/cker/operation/optimized/Gemm.h | 100 + .../cker/include/cker/operation/reference/Conv.h | 85 + .../reference/integer_ops/DepthwiseConvHybrid.h | 122 + .../include/cker/train/operation/FullyConnected.h | 49 + compute/cker/include/cker/train/operation/Loss.h | 77 + compute/cker/include/cker/train/operation/ReLU.h | 50 + compute/cker/src/train/FullyConnected.test.cc | 83 + compute/cker/src/train/Loss.test.cc | 201 + compute/cker/src/train/Relu.test.cc | 107 + compute/ruy/include/ruy/Shape.h | 2 +- docs/conf.py | 2 +- docs/howto/how-to-build-compiler.md | 13 +- docs/howto/how-to-build-runtime.md | 148 +- docs/howto/how-to-cross-build-runtime-for-arm.md | 63 +- docs/release/1.23/index.rst | 13 + docs/release/1.23/release-note-1.23.0.md | 8 + docs/release/1.24/index.rst | 13 + docs/release/1.24/release-note-1.24.0.md | 9 + docs/release/1.25/index.rst | 13 + docs/release/1.25/release-note_1.25.0.md | 17 + docs/release/onert-micro/0.1/release-note-0.1.0.md | 72 + docs/release/onert-micro/1.0/release-note-1.0.0.md | 12 + infra/cmake/packages/AbseilSourceConfig.cmake | 6 +- .../packages/CMSIS-NN-4.1.0/CMSIS-NNConfig.cmake | 14 + .../CMSIS-NN-4.1.0/CMSIS-NNConfigVersion.cmake | 10 + .../TensorFlowSourceConfig.cmake | 19 + .../TensorFlowSourceConfigVersion.cmake | 10 + infra/command/build-docker-image | 6 + infra/debian/compiler/changelog | 19 + infra/debian/compiler/one-compiler.install | 4 + infra/debian/compiler/rules | 2 +- infra/docker/bionic/Dockerfile.aarch64 | 92 + infra/docker/focal/Dockerfile | 46 + infra/docker/focal/Dockerfile.aarch64 | 62 + infra/docker/jammy/Dockerfile.aarch64 | 60 + infra/nncc/Makefile.arm32 | 4 +- .../buildtool/config/config_aarch64-linux.cmake | 13 + .../buildtool/config/config_aarch64-tizen.cmake | 17 + .../buildtool/config/config_armv7hl-tizen.cmake | 29 + .../buildtool/config/config_armv7l-tizen.cmake | 29 + .../cmake/buildtool/config/config_i686-tizen.cmake | 17 + .../buildtool/config/config_x86_64-tizen.cmake | 17 + .../cmake/options/options_aarch64-darwin.cmake | 4 + .../nncc/cmake/options/options_aarch64-linux.cmake | 4 + .../nncc/cmake/options/options_aarch64-tizen.cmake | 4 + .../nncc/cmake/options/options_armv7hl-tizen.cmake | 5 + .../nncc/cmake/options/options_armv7l-tizen.cmake | 5 + infra/nncc/cmake/options/options_i686-tizen.cmake | 3 + .../nncc/cmake/options/options_x86_64-darwin.cmake | 4 + .../nncc/cmake/options/options_x86_64-tizen.cmake | 3 + infra/nnfw/CMakeLists.txt | 4 +- infra/nnfw/cmake/CfgOptionFlags.cmake | 3 + .../nnfw/cmake/buildtool/config/config_linux.cmake | 5 + .../buildtool/cross/toolchain_armv7l-tizen.cmake | 56 - .../cmake/options/options_aarch64-android.cmake | 2 + .../nnfw/cmake/options/options_aarch64-tizen.cmake | 3 + .../nnfw/cmake/options/options_armv7hl-tizen.cmake | 3 + .../nnfw/cmake/options/options_armv7l-tizen.cmake | 3 + infra/nnfw/cmake/options/options_i686-tizen.cmake | 3 + .../nnfw/cmake/options/options_x86_64-tizen.cmake | 3 + infra/nnfw/cmake/packages/LuciConfig.cmake | 43 + infra/packaging/build | 2 +- infra/packaging/preset/20230413 | 66 + infra/packaging/preset/20230413_windows | 80 + infra/packaging/preset/20230907 | 66 + infra/packaging/preset/20230907_windows | 80 + infra/packaging/res/tf2nnpkg.20230413 | 109 + infra/packaging/res/tf2nnpkg.20230907 | 109 + infra/scripts/compiler_modules.sh | 3 +- infra/scripts/docker_build_nncc.sh | 2 +- infra/scripts/docker_collect_nnpkg_resources.sh | 2 +- nnpackage/schema/circle_schema.fbs | 104 +- onert-micro/CMakeLists.txt | 87 +- onert-micro/README.md | 98 +- onert-micro/eval-driver/Driver.cpp | 2 +- onert-micro/externals/CMakeLists.txt | 9 + onert-micro/externals/flatbuffers/base.h | 453 + .../externals/flatbuffers/code_generators.h | 234 + onert-micro/externals/flatbuffers/flatbuffers.h | 3078 +++ onert-micro/externals/flatbuffers/flatc.h | 95 + onert-micro/externals/flatbuffers/flexbuffers.h | 1852 ++ onert-micro/externals/flatbuffers/grpc.h | 361 + onert-micro/externals/flatbuffers/hash.h | 145 + onert-micro/externals/flatbuffers/idl.h | 1145 + onert-micro/externals/flatbuffers/minireflect.h | 507 + onert-micro/externals/flatbuffers/pch/flatc_pch.h | 40 + onert-micro/externals/flatbuffers/pch/pch.h | 39 + onert-micro/externals/flatbuffers/reflection.h | 520 + .../externals/flatbuffers/reflection_generated.h | 1257 + onert-micro/externals/flatbuffers/registry.h | 140 + onert-micro/externals/flatbuffers/stl_emulation.h | 674 + onert-micro/externals/flatbuffers/util.h | 799 + .../gen/circle-generated/circle/schema_generated.h | 24984 +++++++++++++++++++ onert-micro/helpers/GenerateKernelsListHelper.cpp | 14 + onert-micro/luci-interpreter/CMakeLists.txt | 8 + .../include/luci_interpreter/Interpreter.h | 2 +- .../include/luci_interpreter/TrainingSettings.h | 70 + .../include/luci_interpreter/core/Tensor.h | 117 +- .../core/reader/CircleMicroReader.h | 2 + .../include/luci_interpreter/onert-micro-version.h | 26 + .../luci_interpreter/test_models/TestDataBase.h | 46 + .../test_models/abs/FloatAbsKernel.h | 90 + .../test_models/abs/NegAbsKernel.h | 139 + .../test_models/abs/TestDataAbsBase.h | 60 + .../test_models/add/FloatAddKernel.h | 176 + .../test_models/add/IntAddKernel.h | 285 + .../test_models/add/NegAddKernel.h | 148 + .../test_models/add/TestDataAddBase.h | 68 + .../test_models/add_n/FloatAddNKernel.h | 121 + .../test_models/add_n/NegAddNKernel.h | 94 + .../test_models/add_n/TestDataAddNBase.h | 66 + .../test_models/argmax/FloatArgMaxKernel.h | 95 + .../test_models/argmax/NegArgMaxKernel.h | 89 + .../test_models/argmax/TestDataArgMaxBase.h | 60 + .../test_models/argmin/FloatArgMinKernel.h | 95 + .../test_models/argmin/NegArgMinKernel.h | 89 + .../test_models/argmin/TestDataArgMinBase.h | 60 + .../average_pool_2d/FloatAveragePool2DKernel.h | 103 + .../average_pool_2d/NegAveragePool2DKernel.h | 88 + .../average_pool_2d/TestDataAveragePool2DBase.h | 60 + .../concatenation/FloatConcatenationKernel.h | 105 + .../concatenation/IntConcatenationKernel.h | 168 + .../concatenation/NegConcatenationKernel.h | 215 + .../concatenation/TestDataConcatenationBase.h | 63 + .../test_models/conv2d/FloatConv2DKernel.h | 107 + .../test_models/conv2d/NegConv2DKernel.h | 231 + .../test_models/conv2d/TestDataConv2DBase.h | 58 + .../test_models/conv2d/U8Conv2DKernel.h | 117 + .../test_models/div/FloatDivKernel.h | 153 + .../test_models/div/NegDivKernel.h | 149 + .../test_models/div/TestDataDivBase.h | 68 + .../test_models/elu/FloatEluKernel.h | 90 + .../test_models/elu/NegEluKernel.h | 83 + .../test_models/elu/TestDataEluBase.h | 60 + .../test_models/equal/FloatEqualKernel.h | 281 + .../test_models/equal/IntEqualKernel.h | 278 + .../test_models/equal/TestDataEqualBase.h | 68 + .../test_models/exp/FloatExpKernel.h | 90 + .../test_models/exp/NegExpKernel.h | 84 + .../test_models/exp/TestDataExpBase.h | 60 + .../test_models/expand_dims/ExpandDimsKernel.h | 179 + .../luci_interpreter/test_models/fill/FillKernel.h | 114 + .../test_models/fill/NegFillKernel.h | 152 + .../fully_connected/FloatFullyConnectedKernel.h | 115 + .../fully_connected/NegFullyConnectedKernel.h | 218 + .../fully_connected/TestDataFullyConnectedBase.h | 58 + .../fully_connected/U8FullyConnectedKernel.h | 112 + .../test_models/gather/FloatGatherKernel.h | 96 + .../test_models/gather/IntGatherKernel.h | 92 + .../test_models/gather/NegGatherKernel.h | 214 + .../test_models/gather/TestDataGatherBase.h | 60 + .../test_models/greater/FloatGreaterKernel.h | 185 + .../test_models/greater/TestDataGreaterBase.h | 68 + .../greater_equal/FloatGreaterEqualKernel.h | 158 + .../greater_equal/TestDataGreaterEqualBase.h | 68 + .../test_models/leaky_relu/FloatLeakyReLUKernel.h | 92 + .../test_models/leaky_relu/NegLeakyReLUKernel.h | 85 + .../test_models/leaky_relu/TestDataLeakyReLUBase.h | 60 + .../test_models/less/FloatLessKernel.h | 313 + .../test_models/less/IntLessKernel.h | 301 + .../test_models/less/NegTestDataLessKernel.h | 91 + .../test_models/less/QuantLessKernel.h | 156 + .../test_models/less/TestDataLessBase.h | 68 + .../test_models/less_equal/FloatLessEqualKernel.h | 185 + .../test_models/less_equal/TestDataLessEqualBase.h | 68 + .../test_models/logical_and/BoolLogicalAndKernel.h | 104 + .../test_models/logical_and/NegLogicalAndKernel.h | 90 + .../logical_and/TestDataLogicalAndBase.h | 63 + .../test_models/logical_or/BoolLogicalOrKernel.h | 105 + .../test_models/logical_or/NegLogicalOrKernel.h | 90 + .../test_models/logical_or/TestDataLogicalOrBase.h | 63 + .../test_models/logistic/FloatLogisticKernel.h | 90 + .../test_models/logistic/NegLogisticKernel.h | 139 + .../test_models/logistic/TestDataLogisticBase.h | 60 + .../test_models/maxpool2d/FloatMaxPool2DKernel.h | 107 + .../test_models/maxpool2d/NegMaxPool2DKernel.h | 201 + .../test_models/maxpool2d/TestDataMaxPool2DBase.h | 60 + .../test_models/mul/FloatMulKernel.h | 177 + .../test_models/mul/IntMulKernel.h | 172 + .../test_models/mul/NegMulKernel.h | 209 + .../test_models/mul/TestDataMulBase.h | 68 + .../test_models/neg/FloatNegKernel.h | 91 + .../test_models/neg/NegNegKernel.h | 145 + .../test_models/neg/TestDataNegBase.h | 60 + .../test_models/notequal/FloatNotEqualKernel.h | 184 + .../test_models/notequal/TestDataNotEqualBase.h | 68 + .../luci_interpreter/test_models/pack/PackKernel.h | 270 + .../test_models/pack/TestDataPackBase.h | 63 + .../test_models/pad/FloatPadKernel.h | 106 + .../test_models/pad/NegPadKernel.h | 92 + .../test_models/pad/TestDataPadBase.h | 60 + .../test_models/pad_v2/FloatPadV2Kernel.h | 110 + .../test_models/pad_v2/NegPadV2Kernel.h | 97 + .../test_models/pad_v2/TestDataPadV2Base.h | 60 + .../reduce_common/NegReduceProdKernel.h | 150 + .../test_models/reduce_common/ReduceProdKernel.h | 177 + .../reduce_common/TestDataReduceCommonBase.h | 60 + .../test_models/relu/FloatReLUKernel.h | 88 + .../test_models/relu/NegReLUKernel.h | 83 + .../test_models/relu/TestDataReLUBase.h | 60 + .../test_models/relu6/FloatReLU6Kernel.h | 88 + .../test_models/relu6/NegReLU6Kernel.h | 83 + .../test_models/relu6/TestDataReLU6Base.h | 60 + .../test_models/reshape/ReshapeKernel.h | 176 + .../resize_bilinear/FloatResizeBilinearKernel.h | 190 + .../resize_bilinear/NegResizeBilinearKernel.h | 461 + .../resize_bilinear/TestDataResizeBilinearBase.h | 61 + .../resize_bilinear/U8ResizeBilinearKernel.h | 208 + .../test_models/shape/NegShapeKernel.h | 83 + .../test_models/shape/ShapeKernel.h | 113 + .../test_models/slice/FloatSliceKernel.h | 99 + .../test_models/slice/NegSliceKernel.h | 292 + .../test_models/slice/QuantS16SliceKernel.h | 106 + .../test_models/slice/QuantU8SliceKernel.h | 106 + .../test_models/slice/TestDataSliceBase.h | 60 + .../test_models/split/FloatSplitKernel.h | 103 + .../test_models/split/IntSplitKernel.h | 99 + .../test_models/split/TestDataSplitBase.h | 68 + .../test_models/split_v/SplitVKernel.h | 147 + .../test_models/strided_slice/StridedSliceKernel.h | 130 + .../test_models/sub/FloatSubKernel.h | 177 + .../test_models/sub/IntSubKernel.h | 172 + .../test_models/sub/NegSubKernel.h | 209 + .../test_models/sub/TestDataSubBase.h | 68 + .../test_models/tanh/FloatTanhKernel.h | 89 + .../test_models/tanh/NegTanhKernel.h | 83 + .../test_models/tanh/TestDataTanhBase.h | 60 + .../test_models/transpose/TransposeKernel.h | 121 + .../FloatUnidirectionalLSTMKernel.h | 164 + .../QuantS8UnidirectionalLSTM.h | 265 + .../TestDataUnidirectionalLSTMBase.h | 60 + .../test_models/while/NegWhileKernel.h | 137 + .../test_models/while/WhileKernel.h | 162 + .../luci-interpreter/pal/cmsisnn/PALArgMax.h | 34 - .../pal/cmsisnn/PALAveragePool2d.h | 125 - .../pal/cmsisnn/PALBatchToSpaceND.h | 38 - .../luci-interpreter/pal/cmsisnn/PALConv2d.h | 1 - .../luci-interpreter/pal/cmsisnn/PALDepthToSpace.h | 36 - .../pal/cmsisnn/PALDepthwiseConv2d.h | 193 - .../luci-interpreter/pal/cmsisnn/PALDequantize.h | 45 - .../pal/cmsisnn/PALFullyConnected.h | 1 - .../luci-interpreter/pal/cmsisnn/PALL2Normalize.h | 35 - .../luci-interpreter/pal/cmsisnn/PALL2Pool2D.h | 1 - .../luci-interpreter/pal/cmsisnn/PALLeakyRelu.h | 33 - onert-micro/luci-interpreter/pal/cmsisnn/PALMul.h | 1 - .../luci-interpreter/pal/cmsisnn/PALQuantize.h | 45 - .../pal/cmsisnn/PALResizeBilinear.h | 38 - .../pal/cmsisnn/PALResizeNearestNeighbor.h | 38 - onert-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h | 190 - .../luci-interpreter/pal/cmsisnn/PALSoftmax.h | 79 - .../pal/cmsisnn/PALSpaceToBatchND.h | 39 - .../luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h | 36 - onert-micro/luci-interpreter/pal/cmsisnn/PALSub.h | 1 - .../pal/cmsisnn/PALreference_ops.h | 1556 -- .../pal/{cmsisnn/PALElu.h => common/PALAbs.h} | 19 +- .../luci-interpreter/pal/common/PALAddCommon.h | 94 + onert-micro/luci-interpreter/pal/common/PALAddN.h | 47 + .../luci-interpreter/pal/common/PALArgMinMax.h | 73 + .../pal/common/PALAveragePool2DCommon.h | 93 + .../luci-interpreter/pal/common/PALComparisons.h | 176 + .../luci-interpreter/pal/common/PALConcatenation.h | 70 + .../luci-interpreter/pal/common/PALConv2DCommon.h | 201 + onert-micro/luci-interpreter/pal/common/PALDiv.h | 115 + .../luci-interpreter/pal/{mcu => common}/PALElu.h | 17 +- .../pal/{cmsisnn/PALNeg.h => common/PALExp.h} | 25 +- .../pal/common/PALFullyConnectedCommon.h | 103 + .../luci-interpreter/pal/common/PALLogicalCommon.h | 35 + .../luci-interpreter/pal/common/PALLogistic.h | 156 + .../pal/common/PALMaxPool2DCommon.h | 146 + .../luci-interpreter/pal/common/PALMulCommon.h | 115 + .../pal/{linux => common}/PALNeg.h | 27 +- onert-micro/luci-interpreter/pal/common/PALPad.h | 109 + .../luci-interpreter/pal/common/PALReduceCommon.h | 114 + .../luci-interpreter/pal/common/PALReluCommon.h | 41 + .../pal/common/PALResizeBilinear.h | 135 + .../luci-interpreter/pal/common/PALSoftmax.h | 78 + .../luci-interpreter/pal/common/PALStridedSlice.h | 260 + onert-micro/luci-interpreter/pal/common/PALSub.h | 89 + onert-micro/luci-interpreter/pal/common/PALTanh.h | 121 + .../luci-interpreter/pal/common/PALTranspose.h | 104 + .../common/PALUnidirectionalSequenceLSTMCommon.h | 567 + onert-micro/luci-interpreter/pal/common/PALUtils.h | 182 + onert-micro/luci-interpreter/pal/common/Params.h | 214 + .../pal/common/ProcessBroadcastShapes.h | 209 + .../luci-interpreter/pal/linux/KernelsToBuild.lst | 77 - onert-micro/luci-interpreter/pal/linux/PALArgMax.h | 34 - .../luci-interpreter/pal/linux/PALAveragePool2d.h | 74 - .../luci-interpreter/pal/linux/PALBatchMatMul.h | 68 - .../luci-interpreter/pal/linux/PALBatchToSpaceND.h | 38 - onert-micro/luci-interpreter/pal/linux/PALConv2d.h | 128 - .../luci-interpreter/pal/linux/PALDepthToSpace.h | 36 - .../pal/linux/PALDepthwiseConv2d.h | 92 - .../luci-interpreter/pal/linux/PALDequantize.h | 35 - .../luci-interpreter/pal/linux/PALFullyConnected.h | 62 - onert-micro/luci-interpreter/pal/linux/PALGather.h | 36 - .../luci-interpreter/pal/linux/PALL2Normalize.h | 35 - .../luci-interpreter/pal/linux/PALL2Pool2D.h | 34 - .../pal/linux/PALLocalResponseNormalization.h | 35 - .../luci-interpreter/pal/linux/PALLogSoftmax.h | 48 - onert-micro/luci-interpreter/pal/linux/PALMul.h | 56 - .../luci-interpreter/pal/linux/PALQuantize.h | 45 - onert-micro/luci-interpreter/pal/linux/PALRelu.h | 40 - onert-micro/luci-interpreter/pal/linux/PALRelu6.h | 40 - .../luci-interpreter/pal/linux/PALResizeBilinear.h | 38 - .../pal/linux/PALResizeNearestNeighbor.h | 38 - onert-micro/luci-interpreter/pal/linux/PALSVDF.h | 91 - onert-micro/luci-interpreter/pal/linux/PALSlice.h | 34 - .../luci-interpreter/pal/linux/PALSoftmax.h | 48 - .../luci-interpreter/pal/linux/PALSpaceToBatchND.h | 39 - .../luci-interpreter/pal/linux/PALSpaceToDepth.h | 36 - onert-micro/luci-interpreter/pal/linux/PALSplit.h | 34 - onert-micro/luci-interpreter/pal/linux/PALSub.h | 36 - onert-micro/luci-interpreter/pal/linux/pal.cmake | 82 - .../luci-interpreter/pal/mcu/KernelsToBuild.lst | 38 + onert-micro/luci-interpreter/pal/mcu/PALAdd.h | 41 + .../pal/mcu/PALApplyActivationToVector.h | 75 - onert-micro/luci-interpreter/pal/mcu/PALArgMax.h | 34 - .../luci-interpreter/pal/mcu/PALAveragePool2D.h | 28 + .../luci-interpreter/pal/mcu/PALAveragePool2d.h | 74 - .../luci-interpreter/pal/mcu/PALBatchToSpaceND.h | 38 - onert-micro/luci-interpreter/pal/mcu/PALConv2d.h | 66 +- .../luci-interpreter/pal/mcu/PALDepthToSpace.h | 36 - .../luci-interpreter/pal/mcu/PALDepthwiseConv2d.h | 92 - .../luci-interpreter/pal/mcu/PALDequantize.h | 45 - .../luci-interpreter/pal/mcu/PALFullyConnected.h | 61 +- .../luci-interpreter/pal/mcu/PALL2Normalize.h | 35 - onert-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h | 1 - .../luci-interpreter/pal/mcu/PALLeakyRelu.h | 33 - .../luci-interpreter/pal/mcu/PALMaxPool2D.h | 28 + onert-micro/luci-interpreter/pal/mcu/PALMul.h | 26 +- onert-micro/luci-interpreter/pal/mcu/PALNeg.h | 33 - onert-micro/luci-interpreter/pal/mcu/PALQuantize.h | 45 - .../luci-interpreter/pal/mcu/PALResizeBilinear.h | 38 - .../pal/mcu/PALResizeNearestNeighbor.h | 39 - onert-micro/luci-interpreter/pal/mcu/PALSVDF.h | 258 - onert-micro/luci-interpreter/pal/mcu/PALSoftmax.h | 59 - .../luci-interpreter/pal/mcu/PALSpaceToBatchND.h | 39 - .../luci-interpreter/pal/mcu/PALSpaceToDepth.h | 36 - onert-micro/luci-interpreter/pal/mcu/PALSub.h | 36 - .../pal/mcu/PALUnidirectionalSequenceLSTM.h | 616 +- .../luci-interpreter/pal/mcu/PALreference_ops.h | 1556 -- onert-micro/luci-interpreter/pal/mcu/pal.cmake | 51 +- onert-micro/luci-interpreter/src/Interpreter.cpp | 4 +- .../luci-interpreter/src/core/RuntimeGraph.cpp | 187 +- .../luci-interpreter/src/core/RuntimeGraph.h | 52 +- .../luci-interpreter/src/core/RuntimeModule.h | 5 +- .../src/core/reader/CircleMicroReader.cpp | 2 + onert-micro/luci-interpreter/src/kernels/Abs.cpp | 90 + .../luci-interpreter/src/kernels/Abs.test.cpp | 99 + onert-micro/luci-interpreter/src/kernels/Add.cpp | 259 +- onert-micro/luci-interpreter/src/kernels/Add.h | 51 - .../luci-interpreter/src/kernels/Add.test.cpp | 381 +- onert-micro/luci-interpreter/src/kernels/AddN.cpp | 113 + .../luci-interpreter/src/kernels/AddN.test.cpp | 100 + .../luci-interpreter/src/kernels/ArgMax.cpp | 138 +- onert-micro/luci-interpreter/src/kernels/ArgMax.h | 45 - .../luci-interpreter/src/kernels/ArgMax.test.cpp | 127 +- .../luci-interpreter/src/kernels/ArgMin.cpp | 70 + .../luci-interpreter/src/kernels/ArgMin.test.cpp | 86 + .../luci-interpreter/src/kernels/AveragePool2D.cpp | 218 +- .../luci-interpreter/src/kernels/AveragePool2D.h | 55 - .../src/kernels/AveragePool2D.test.cpp | 280 +- .../luci-interpreter/src/kernels/BatchMatMul.h | 1 - .../src/kernels/BatchMatMul.test.cpp | 1 - .../luci-interpreter/src/kernels/BatchToSpaceND.h | 1 - .../src/kernels/BatchToSpaceND.test.cpp | 1 - .../luci-interpreter/src/kernels/BinaryOpCommon.h | 212 +- .../luci-interpreter/src/kernels/Builders.h | 16 +- .../luci-interpreter/src/kernels/CMakeLists.txt | 12 +- onert-micro/luci-interpreter/src/kernels/Cast.h | 1 - .../luci-interpreter/src/kernels/Cast.test.cpp | 1 - .../luci-interpreter/src/kernels/Concatenation.cpp | 57 +- .../src/kernels/Concatenation.test.cpp | 280 +- .../luci-interpreter/src/kernels/Conv2D.cpp | 197 +- .../luci-interpreter/src/kernels/Conv2D.test.cpp | 712 +- .../luci-interpreter/src/kernels/DepthToSpace.cpp | 1 - .../luci-interpreter/src/kernels/DepthToSpace.h | 1 - .../src/kernels/DepthToSpace.test.cpp | 1 - .../luci-interpreter/src/kernels/DepthwiseConv2D.h | 1 - .../src/kernels/DepthwiseConv2D.test.cpp | 1 - .../luci-interpreter/src/kernels/Dequantize.cpp | 1 - .../luci-interpreter/src/kernels/Dequantize.h | 1 - onert-micro/luci-interpreter/src/kernels/Div.cpp | 159 +- onert-micro/luci-interpreter/src/kernels/Div.h | 50 - .../luci-interpreter/src/kernels/Div.test.cpp | 244 +- onert-micro/luci-interpreter/src/kernels/Elu.cpp | 58 +- .../luci-interpreter/src/kernels/Elu.test.cpp | 92 +- onert-micro/luci-interpreter/src/kernels/Equal.cpp | 143 +- onert-micro/luci-interpreter/src/kernels/Equal.h | 55 - .../luci-interpreter/src/kernels/Equal.test.cpp | 309 +- onert-micro/luci-interpreter/src/kernels/Exp.cpp | 63 +- .../luci-interpreter/src/kernels/Exp.test.cpp | 80 +- .../luci-interpreter/src/kernels/ExpandDims.cpp | 6 +- .../src/kernels/ExpandDims.test.cpp | 112 +- onert-micro/luci-interpreter/src/kernels/Fill.cpp | 115 +- onert-micro/luci-interpreter/src/kernels/Fill.h | 48 - .../luci-interpreter/src/kernels/Fill.test.cpp | 169 +- onert-micro/luci-interpreter/src/kernels/Floor.h | 1 - .../luci-interpreter/src/kernels/Floor.test.cpp | 1 - .../luci-interpreter/src/kernels/FloorDiv.h | 1 - .../src/kernels/FullyConnected.cpp | 96 +- .../src/kernels/FullyConnected.test.cpp | 285 +- .../luci-interpreter/src/kernels/Gather.cpp | 173 +- onert-micro/luci-interpreter/src/kernels/Gather.h | 48 - .../luci-interpreter/src/kernels/Gather.test.cpp | 166 +- .../luci-interpreter/src/kernels/Greater.cpp | 146 +- .../luci-interpreter/src/kernels/Greater.test.cpp | 327 +- .../luci-interpreter/src/kernels/GreaterEqual.cpp | 149 +- .../luci-interpreter/src/kernels/GreaterEqual.h | 55 - .../src/kernels/GreaterEqual.test.cpp | 326 +- onert-micro/luci-interpreter/src/kernels/If.cpp | 1 - onert-micro/luci-interpreter/src/kernels/If.h | 1 - .../luci-interpreter/src/kernels/InstanceNorm.cpp | 1 - .../luci-interpreter/src/kernels/InstanceNorm.h | 1 - .../src/kernels/InstanceNorm.test.cpp | 1 - .../luci-interpreter/src/kernels/KernelBuilder.cpp | 38 +- .../luci-interpreter/src/kernels/KernelBuilder.h | 109 +- .../luci-interpreter/src/kernels/L2Normalize.cpp | 1 - .../luci-interpreter/src/kernels/L2Normalize.h | 1 - .../luci-interpreter/src/kernels/L2Pool2D.h | 1 - .../luci-interpreter/src/kernels/LeakyRelu.cpp | 97 +- .../luci-interpreter/src/kernels/LeakyRelu.h | 54 - .../src/kernels/LeakyRelu.test.cpp | 129 +- onert-micro/luci-interpreter/src/kernels/Less.cpp | 181 +- .../luci-interpreter/src/kernels/Less.test.cpp | 347 +- .../luci-interpreter/src/kernels/LessEqual.cpp | 147 +- .../luci-interpreter/src/kernels/LessEqual.h | 55 - .../src/kernels/LessEqual.test.cpp | 327 +- .../src/kernels/LocalResponseNormalization.cpp | 1 - .../src/kernels/LocalResponseNormalization.h | 1 - .../luci-interpreter/src/kernels/LogSoftmax.cpp | 1 - .../luci-interpreter/src/kernels/LogSoftmax.h | 1 - .../luci-interpreter/src/kernels/LogicalAnd.cpp | 70 +- .../luci-interpreter/src/kernels/LogicalAnd.h | 48 - .../src/kernels/LogicalAnd.test.cpp | 114 +- .../luci-interpreter/src/kernels/LogicalNot.cpp | 1 - .../luci-interpreter/src/kernels/LogicalNot.h | 47 - .../luci-interpreter/src/kernels/LogicalOr.cpp | 61 +- .../luci-interpreter/src/kernels/LogicalOr.h | 44 - .../src/kernels/LogicalOr.test.cpp | 117 +- .../luci-interpreter/src/kernels/Logistic.cpp | 83 +- .../luci-interpreter/src/kernels/Logistic.test.cpp | 164 +- .../luci-interpreter/src/kernels/MISOKernel.h | 87 + .../luci-interpreter/src/kernels/MaxPool2D.cpp | 181 +- .../src/kernels/MaxPool2D.test.cpp | 181 +- onert-micro/luci-interpreter/src/kernels/Maximum.h | 1 - onert-micro/luci-interpreter/src/kernels/Mean.h | 1 - onert-micro/luci-interpreter/src/kernels/Minimum.h | 1 - .../luci-interpreter/src/kernels/MirrorPad.h | 1 - .../src/kernels/MirrorPad.test.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Mul.cpp | 222 +- onert-micro/luci-interpreter/src/kernels/Mul.h | 53 - .../luci-interpreter/src/kernels/Mul.test.cpp | 313 +- onert-micro/luci-interpreter/src/kernels/Neg.cpp | 58 +- onert-micro/luci-interpreter/src/kernels/Neg.h | 47 - .../luci-interpreter/src/kernels/Neg.test.cpp | 97 +- .../luci-interpreter/src/kernels/NotEqual.cpp | 147 +- .../luci-interpreter/src/kernels/NotEqual.h | 55 - .../luci-interpreter/src/kernels/NotEqual.test.cpp | 299 +- onert-micro/luci-interpreter/src/kernels/OneHot.h | 1 - .../luci-interpreter/src/kernels/OneHot.test.cpp | 1 - onert-micro/luci-interpreter/src/kernels/PRelu.cpp | 1 - onert-micro/luci-interpreter/src/kernels/PRelu.h | 1 - onert-micro/luci-interpreter/src/kernels/Pack.cpp | 160 +- onert-micro/luci-interpreter/src/kernels/Pack.h | 47 - .../luci-interpreter/src/kernels/Pack.test.cpp | 165 +- onert-micro/luci-interpreter/src/kernels/Pad.cpp | 95 +- onert-micro/luci-interpreter/src/kernels/Pad.h | 44 - .../luci-interpreter/src/kernels/Pad.test.cpp | 122 +- .../luci-interpreter/src/kernels/PadCommon.cpp | 144 + .../{loader/nodes/Neg.cpp => kernels/PadCommon.h} | 24 +- onert-micro/luci-interpreter/src/kernels/PadV2.cpp | 91 +- onert-micro/luci-interpreter/src/kernels/PadV2.h | 45 - .../luci-interpreter/src/kernels/PadV2.test.cpp | 111 +- onert-micro/luci-interpreter/src/kernels/Pow.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Pow.h | 1 - .../luci-interpreter/src/kernels/Pow.test.cpp | 1 - .../luci-interpreter/src/kernels/Quantize.cpp | 1 - .../luci-interpreter/src/kernels/Quantize.h | 1 - .../luci-interpreter/src/kernels/ReduceCommon.cpp | 92 + .../src/kernels/ReduceCommon.test.cpp | 107 + onert-micro/luci-interpreter/src/kernels/Relu.cpp | 116 +- onert-micro/luci-interpreter/src/kernels/Relu.h | 52 - .../luci-interpreter/src/kernels/Relu.test.cpp | 163 +- onert-micro/luci-interpreter/src/kernels/Relu6.cpp | 90 +- .../luci-interpreter/src/kernels/Relu6.test.cpp | 144 +- .../luci-interpreter/src/kernels/Reshape.cpp | 53 +- .../luci-interpreter/src/kernels/Reshape.test.cpp | 85 +- .../src/kernels/ResizeBilinear.cpp | 109 +- .../luci-interpreter/src/kernels/ResizeBilinear.h | 1 - .../src/kernels/ResizeBilinear.test.cpp | 323 +- .../src/kernels/ResizeNearestNeighbor.h | 1 - .../luci-interpreter/src/kernels/ReverseV2.cpp | 1 - .../luci-interpreter/src/kernels/ReverseV2.h | 1 - onert-micro/luci-interpreter/src/kernels/Rsqrt.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Rsqrt.h | 1 - .../luci-interpreter/src/kernels/Rsqrt.test.cpp | 1 - .../luci-interpreter/src/kernels/SISOKernel.h | 57 + onert-micro/luci-interpreter/src/kernels/SVDF.h | 1 - .../luci-interpreter/src/kernels/SVDF.test.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Shape.cpp | 55 +- .../luci-interpreter/src/kernels/Shape.test.cpp | 86 +- onert-micro/luci-interpreter/src/kernels/Slice.cpp | 243 +- onert-micro/luci-interpreter/src/kernels/Slice.h | 45 - .../luci-interpreter/src/kernels/Slice.test.cpp | 142 +- .../luci-interpreter/src/kernels/Softmax.cpp | 88 +- .../luci-interpreter/src/kernels/Softmax.test.cpp | 1 - .../luci-interpreter/src/kernels/SpaceToBatchND.h | 1 - .../src/kernels/SpaceToBatchND.test.cpp | 1 - .../luci-interpreter/src/kernels/SpaceToDepth.cpp | 1 - .../luci-interpreter/src/kernels/SpaceToDepth.h | 1 - .../src/kernels/SpaceToDepth.test.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Split.cpp | 107 +- onert-micro/luci-interpreter/src/kernels/Split.h | 73 +- .../luci-interpreter/src/kernels/Split.test.cpp | 130 +- .../luci-interpreter/src/kernels/SplitV.cpp | 121 +- onert-micro/luci-interpreter/src/kernels/SplitV.h | 50 - .../luci-interpreter/src/kernels/SplitV.test.cpp | 113 +- onert-micro/luci-interpreter/src/kernels/Sqrt.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Sqrt.h | 1 - .../luci-interpreter/src/kernels/Sqrt.test.cpp | 1 - .../luci-interpreter/src/kernels/Square.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Square.h | 1 - .../src/kernels/SquaredDifference.h | 1 - .../luci-interpreter/src/kernels/Squeeze.test.cpp | 1 - .../luci-interpreter/src/kernels/StridedSlice.cpp | 189 +- .../luci-interpreter/src/kernels/StridedSlice.h | 48 - .../src/kernels/StridedSlice.test.cpp | 114 +- onert-micro/luci-interpreter/src/kernels/Sub.cpp | 245 +- onert-micro/luci-interpreter/src/kernels/Sub.h | 50 - .../luci-interpreter/src/kernels/Sub.test.cpp | 288 +- .../luci-interpreter/src/kernels/TISOKernel.h | 127 + onert-micro/luci-interpreter/src/kernels/Tanh.cpp | 209 +- onert-micro/luci-interpreter/src/kernels/Tanh.h | 53 - .../luci-interpreter/src/kernels/Tanh.test.cpp | 169 +- .../luci-interpreter/src/kernels/TestUtils.cpp | 83 - .../luci-interpreter/src/kernels/TestUtils.h | 253 - .../luci-interpreter/src/kernels/Transpose.cpp | 87 +- .../luci-interpreter/src/kernels/Transpose.h | 45 - .../src/kernels/Transpose.test.cpp | 111 +- .../luci-interpreter/src/kernels/TransposeConv.h | 1 - .../src/kernels/TransposeConv.test.cpp | 1 - .../src/kernels/UnidirectionalSequenceLSTM.cpp | 31 +- .../src/kernels/UnidirectionalSequenceLSTM.h | 39 +- .../kernels/UnidirectionalSequenceLSTM.test.cpp | 556 +- .../luci-interpreter/src/kernels/Unpack.cpp | 1 - onert-micro/luci-interpreter/src/kernels/Unpack.h | 1 - onert-micro/luci-interpreter/src/kernels/Utils.cpp | 48 +- onert-micro/luci-interpreter/src/kernels/Utils.h | 111 +- onert-micro/luci-interpreter/src/kernels/While.cpp | 202 +- onert-micro/luci-interpreter/src/kernels/While.h | 49 - .../luci-interpreter/src/kernels/While.test.cpp | 104 +- .../luci-interpreter/src/loader/GraphLoader.cpp | 130 +- .../luci-interpreter/src/loader/ModuleLoader.cpp | 16 +- .../luci-interpreter/src/loader/ModuleLoader.h | 2 +- .../luci-interpreter/src/loader/nodes/Add.cpp | 44 - .../luci-interpreter/src/loader/nodes/ArgMax.cpp | 44 - .../src/loader/nodes/AveragePool2D.cpp | 57 - .../src/loader/nodes/BatchMatMul.cpp | 55 - .../src/loader/nodes/BatchToSpaceND.cpp | 39 - .../src/loader/nodes/Concatenation.cpp | 48 - .../luci-interpreter/src/loader/nodes/Conv2D.cpp | 58 - .../src/loader/nodes/DepthToSpace.cpp | 44 - .../src/loader/nodes/DepthwiseConv2D.cpp | 60 - .../src/loader/nodes/Dequantize.cpp | 35 - .../luci-interpreter/src/loader/nodes/Div.cpp | 43 - .../luci-interpreter/src/loader/nodes/Elu.cpp | 35 - .../luci-interpreter/src/loader/nodes/Equal.cpp | 38 - .../src/loader/nodes/ExpandDims.cpp | 37 - .../luci-interpreter/src/loader/nodes/Fill.cpp | 36 - .../luci-interpreter/src/loader/nodes/FloorDiv.cpp | 36 - .../src/loader/nodes/FullyConnected.cpp | 47 - .../luci-interpreter/src/loader/nodes/Gather.cpp | 45 - .../luci-interpreter/src/loader/nodes/Greater.cpp | 36 - .../src/loader/nodes/GreaterEqual.cpp | 37 - .../src/loader/nodes/InstanceNorm.cpp | 48 - .../src/loader/nodes/L2Normalize.cpp | 44 - .../luci-interpreter/src/loader/nodes/L2Pool2D.cpp | 48 - .../src/loader/nodes/LeakyRelu.cpp | 44 - .../luci-interpreter/src/loader/nodes/Less.cpp | 37 - .../loader/nodes/LocalResponseNormalization.cpp | 46 - .../src/loader/nodes/LogSoftmax.cpp | 36 - .../src/loader/nodes/LogicalAnd.cpp | 38 - .../src/loader/nodes/LogicalNot.cpp | 36 - .../src/loader/nodes/LogicalOr.cpp | 38 - .../luci-interpreter/src/loader/nodes/Logistic.cpp | 36 - .../src/loader/nodes/MaxPool2D.cpp | 49 - .../luci-interpreter/src/loader/nodes/Maximum.cpp | 37 - .../luci-interpreter/src/loader/nodes/Mean.cpp | 57 - .../luci-interpreter/src/loader/nodes/Minimum.cpp | 37 - .../src/loader/nodes/MirrorPad.cpp | 45 - .../luci-interpreter/src/loader/nodes/Mul.cpp | 44 - .../luci-interpreter/src/loader/nodes/NotEqual.cpp | 37 - .../luci-interpreter/src/loader/nodes/OneHot.cpp | 46 - .../luci-interpreter/src/loader/nodes/Pack.cpp | 46 - .../luci-interpreter/src/loader/nodes/Pad.cpp | 37 - .../luci-interpreter/src/loader/nodes/PadV2.cpp | 38 - .../luci-interpreter/src/loader/nodes/Pow.cpp | 37 - .../luci-interpreter/src/loader/nodes/Quantize.cpp | 35 - .../luci-interpreter/src/loader/nodes/Relu.cpp | 35 - .../luci-interpreter/src/loader/nodes/Relu6.cpp | 35 - .../luci-interpreter/src/loader/nodes/Reshape.cpp | 38 - .../src/loader/nodes/ResizeBilinear.cpp | 46 - .../src/loader/nodes/ResizeNearestNeighbor.cpp | 50 - .../src/loader/nodes/ReverseV2.cpp | 38 - .../luci-interpreter/src/loader/nodes/Rsqrt.cpp | 35 - .../luci-interpreter/src/loader/nodes/SVDF.cpp | 89 - .../luci-interpreter/src/loader/nodes/Shape.cpp | 42 - .../luci-interpreter/src/loader/nodes/Slice.cpp | 38 - .../luci-interpreter/src/loader/nodes/Softmax.cpp | 42 - .../src/loader/nodes/SpaceToBatchND.cpp | 39 - .../src/loader/nodes/SpaceToDepth.cpp | 43 - .../luci-interpreter/src/loader/nodes/Split.cpp | 43 - .../luci-interpreter/src/loader/nodes/SplitV.cpp | 44 - .../luci-interpreter/src/loader/nodes/Sqrt.cpp | 35 - .../luci-interpreter/src/loader/nodes/Square.cpp | 35 - .../src/loader/nodes/SquaredDifference.cpp | 38 - .../luci-interpreter/src/loader/nodes/Squeeze.cpp | 42 - .../src/loader/nodes/StridedSlice.cpp | 51 - .../luci-interpreter/src/loader/nodes/Sub.cpp | 44 - .../luci-interpreter/src/loader/nodes/Tanh.cpp | 35 - .../src/loader/nodes/Transpose.cpp | 38 - .../src/loader/nodes/TransposeConv.cpp | 57 - .../loader/nodes/UnidirectionalSequenceLSTM.cpp | 145 - .../luci-interpreter/src/loader/nodes/Unpack.cpp | 49 - onert-micro/standalone/CMakeLists.txt | 8 +- packaging/ABSEIL.tar.gz | Bin 1909045 -> 2151177 bytes packaging/FLATBUFFERS-2.0.tar.gz | Bin 345691 -> 407104 bytes packaging/nnfw.spec | 70 +- res/CircleSchema/0.5/circle_schema.fbs | 1338 + res/CircleSchema/0.6/circle_schema.fbs | 1388 ++ res/PyTorchExamples/ptem.py | 6 +- res/TensorFlowLiteRecipes/Gelu_000/test.recipe | 20 + res/TensorFlowLiteRecipes/Gelu_000/test.reverse | 0 .../HardSwish_000/test.recipe | 17 + .../HardSwish_000/test.reverse | 0 .../HardSwish_001/test.recipe | 17 + res/TensorFlowLiteRecipes/HardSwish_001/test.rule | 8 + res/TensorFlowLiteRecipes/Net_Gelu_000/test.recipe | 100 + res/TensorFlowLiteRecipes/Net_Gelu_000/test.rule | 8 + res/TensorFlowLiteRecipes/Net_Gelu_001/test.recipe | 100 + res/TensorFlowLiteRecipes/Net_Gelu_001/test.rule | 8 + .../Net_TConv_Add_000/test.recipe | 1 + .../Net_TConv_Add_001/test.recipe | 1 + .../Net_TConv_Add_002/test.recipe | 1 + .../Net_TConv_BN_000/test.recipe | 1 + .../Net_TConv_BN_001/test.recipe | 1 + .../Net_TConv_BN_002/test.recipe | 1 + .../Net_TConv_BN_003/test.recipe | 1 + .../Net_TConv_BN_004/test.recipe | 1 + .../Net_TConv_BN_005/test.recipe | 1 + .../Quant_Add_I8_000/test.recipe | 66 + .../Quant_Add_I8_000/test.rule | 7 + .../Quant_AveragePool2D_I8_000/test.recipe | 51 + .../Quant_AveragePool2D_I8_000/test.rule | 6 + .../Quant_Conv_I8_000/test.recipe | 472 + .../Quant_Conv_I8_000/test.rule | 8 + .../Quant_DepthwiseConv2D_I8_000/test.recipe | 473 + .../Quant_DepthwiseConv2D_I8_000/test.rule | 8 + .../Quant_MaxPool2D_I8_000/test.recipe | 51 + .../Quant_MaxPool2D_I8_000/test.rule | 6 + .../Quant_Mean_I8_000/test.recipe | 63 + .../Quant_Mean_I8_000/test.rule | 7 + .../Quant_Mul_I8_000/test.recipe | 66 + .../Quant_Mul_I8_000/test.rule | 7 + .../Quant_PRelu_I8_000/test.recipe | 66 + .../Quant_PRelu_I8_000/test.rule | 7 + .../Quant_ReLU_I8_000/test.recipe | 43 + .../Quant_ReLU_I8_000/test.rule | 6 + .../Quant_TransposeConv_000/test.recipe | 1 + .../Quant_TransposeConv_001/test.recipe | 1 + .../Quant_TransposeConv_I8_000/test.recipe | 344 + .../Quant_TransposeConv_I8_000/test.rule | 8 + .../REGRESS_ONNX_Mul_Mul_000/test.recipe | 88 + .../REGRESS_ONNX_Mul_Mul_000/test.rule | 7 + .../TransposeConv_000/test.recipe | 1 + .../TransposeConv_001/test.recipe | 1 + res/TensorFlowLiteSchema/2.10.1/schema.fbs | 1306 + res/TensorFlowLiteSchema/2.12.1/schema.fbs | 1340 + res/TensorFlowLiteSchema/SCHEMA.lst | 1 + runtime/contrib/android/api/build.gradle | 2 +- .../libs/benchmark/include/benchmark/CsvWriter.h | 1 + runtime/libs/benchmark/include/benchmark/Phase.h | 1 + runtime/libs/benchmark/src/Result.cpp | 2 +- runtime/onert/CMakeLists.txt | 1 + runtime/onert/api/include/nnfw.h | 10 +- runtime/onert/api/include/nnfw_experimental.h | 244 + runtime/onert/api/include/nnfw_version.h | 2 +- runtime/onert/api/src/nnfw_api.cc | 130 + runtime/onert/api/src/nnfw_api_internal.cc | 440 +- runtime/onert/api/src/nnfw_api_internal.h | 46 +- runtime/onert/backend/CMakeLists.txt | 7 + runtime/onert/backend/acl_cl/Config.cc | 2 +- runtime/onert/backend/acl_cl/Config.h | 2 +- runtime/onert/backend/acl_cl/KernelGenerator.cc | 2 +- runtime/onert/backend/acl_cl/Optimizer.cc | 2 +- .../onert/backend/acl_common/AclTensorManager.h | 6 +- runtime/onert/backend/acl_neon/Config.cc | 2 +- runtime/onert/backend/acl_neon/Config.h | 2 +- runtime/onert/backend/acl_neon/KernelGenerator.cc | 2 +- runtime/onert/backend/acl_neon/Optimizer.cc | 2 +- .../cl_common/include/cl_common/BackendContext.h | 6 +- runtime/onert/backend/cpu/BackendContext.cc | 2 +- runtime/onert/backend/cpu/CMakeLists.txt | 4 +- runtime/onert/backend/cpu/Config.cc | 2 +- runtime/onert/backend/cpu/Config.h | 2 +- runtime/onert/backend/cpu/KernelGenerator.cc | 2 +- runtime/onert/backend/cpu/ops/ConvolutionLayer.cc | 74 +- runtime/onert/backend/cpu/ops/ConvolutionLayer.h | 22 +- .../backend/cpu/ops/DepthwiseConvolutionLayer.cc | 86 +- .../backend/cpu/ops/DepthwiseConvolutionLayer.h | 9 + .../backend/cpu/ops/ElementwiseActivationLayer.h | 2 +- .../onert/backend/cpu/ops/FullyConnectedLayer.cc | 9 + .../onert/backend/cpu/ops/FullyConnectedLayer.h | 2 +- runtime/onert/backend/cpu/ops/OperationUtils.cc | 2 +- runtime/onert/backend/gpu_cl/BackendContext.cc | 2 +- runtime/onert/backend/gpu_cl/Config.cc | 2 +- runtime/onert/backend/gpu_cl/Config.h | 2 +- runtime/onert/backend/gpu_cl/TensorManager.cc | 4 +- runtime/onert/backend/ruy/BackendContext.cc | 2 +- runtime/onert/backend/ruy/Config.cc | 2 +- runtime/onert/backend/ruy/Config.h | 2 +- runtime/onert/backend/ruy/KernelGenerator.cc | 2 +- runtime/onert/backend/train/Backend.h | 72 + runtime/onert/backend/train/BackendContext.cc | 112 + runtime/onert/backend/train/BackendContext.h | 90 + runtime/onert/backend/train/CMakeLists.txt | 20 + runtime/onert/backend/train/Config.cc | 32 + runtime/onert/backend/train/Config.h | 47 + runtime/onert/backend/train/ExternalContext.h | 35 + runtime/onert/backend/train/KernelGenerator.cc | 260 + runtime/onert/backend/train/KernelGenerator.h | 67 + runtime/onert/backend/train/MemoryManager.h | 35 + runtime/onert/backend/train/Tensor.h | 55 + runtime/onert/backend/train/TensorBuilder.cc | 138 + runtime/onert/backend/train/TensorBuilder.h | 77 + runtime/onert/backend/train/TensorManager.cc | 155 + runtime/onert/backend/train/TensorManager.h | 66 + runtime/onert/backend/train/TensorRegistry.h | 38 + .../onert/backend/train/ops/ConvolutionLayer.cc | 57 + runtime/onert/backend/train/ops/ConvolutionLayer.h | 56 + .../train/ops/ElementwiseActivationLayer.cc | 95 + .../backend/train/ops/ElementwiseActivationLayer.h | 66 + .../onert/backend/train/ops/FullyConnectedLayer.cc | 209 + .../onert/backend/train/ops/FullyConnectedLayer.h | 74 + runtime/onert/backend/train/ops/GradientApplier.cc | 52 + runtime/onert/backend/train/ops/GradientApplier.h | 54 + runtime/onert/backend/train/ops/LossLayer.cc | 99 + runtime/onert/backend/train/ops/LossLayer.h | 62 + runtime/onert/backend/train/ops/OperationUtils.h | 40 + runtime/onert/backend/train/ops/PoolLayer.cc | 72 + runtime/onert/backend/train/ops/PoolLayer.h | 59 + runtime/onert/backend/train/ops/ReshapeLayer.cc | 61 + runtime/onert/backend/train/ops/ReshapeLayer.h | 62 + runtime/onert/backend/train/train.cc | 24 + runtime/onert/backend/trix/BackendContext.cc | 2 +- runtime/onert/backend/trix/Config.cc | 2 +- runtime/onert/backend/trix/Config.h | 2 +- runtime/onert/backend/trix/DevContext.cc | 12 +- runtime/onert/backend/xnnpack/BackendContext.cc | 2 +- runtime/onert/backend/xnnpack/Config.cc | 2 +- runtime/onert/backend/xnnpack/Config.h | 2 +- runtime/onert/backend/xnnpack/KernelGenerator.cc | 2 +- runtime/onert/core/CMakeLists.txt | 31 +- runtime/onert/core/include/backend/IConfig.h | 6 +- .../include/backend/basic/BackendContextHelpers.h | 15 +- .../include/backend/basic/DynamicTensorManager.h | 4 +- .../include/backend/basic/StaticTensorManager.h | 2 + .../core/include/backend/basic/TensorBuilder.h | 1 + .../basic/train/TrainableBackendContextHelpers.h | 68 + .../include/backend/basic/train/TrainableTensor.h | 98 + .../core/include/backend/train/ITensorRegistry.h | 246 + .../core/include/backend/train/ITrainableBackend.h | 42 + .../core/include/backend/train/ITrainableTensor.h | 53 + .../include/backend/train/KernelGeneratorBase.h | 60 + .../backend/train/TrainableBackendContext.h | 100 + runtime/onert/core/include/compiler/CodeMap.h | 6 +- .../onert/core/include/compiler/CompilerFactory.h | 4 +- .../onert/core/include/compiler/CompilerOptions.h | 1 + .../onert/core/include/compiler/ILoweredGraph.h | 42 + runtime/onert/core/include/compiler/LoweredGraph.h | 19 +- .../core/include/compiler/StaticShapeInferer.h | 17 +- .../include/compiler/train/LoweredTrainableGraph.h | 80 + .../core/include/compiler/train/TrainableCodeMap.h | 54 + .../core/include/compiler/train/TrainingInfo.h | 71 + runtime/onert/core/include/exec/Execution.h | 20 + runtime/onert/core/include/exec/FunctionSequence.h | 2 +- runtime/onert/core/include/exec/MinMaxMap.h | 39 + .../core/include/exec/train/IGradientApplier.h | 46 + .../core/include/exec/train/ITrainableFunction.h | 41 + .../core/include/exec/train/TrainableFnSequence.h | 52 + .../core/include/exec/train/optimizer/Optimizer.h | 77 + .../include/exec/train/optimizer/OptimizerCode.h | 53 + .../onert/core/include/exec/train/optimizer/SGD.h | 83 + runtime/onert/core/include/ir/Graph.h | 68 +- runtime/onert/core/include/ir/IGraph.h | 47 + runtime/onert/core/include/ir/IOperation.h | 50 + runtime/onert/core/include/ir/Model.h | 68 +- runtime/onert/core/include/ir/NNPkg.h | 30 +- .../onert/core/include/ir/OperandIndexSequence.h | 1 + runtime/onert/core/include/ir/Operation.h | 22 +- runtime/onert/core/include/ir/Operations.Include.h | 1 + runtime/onert/core/include/ir/Operations.h | 4 +- runtime/onert/core/include/ir/Operations.lst | 3 + .../core/include/ir/operation/BinaryArithmetic.h | 2 +- runtime/onert/core/include/ir/operation/Loss.h | 74 + .../core/include/ir/train/ITrainableOperation.h | 49 + .../core/include/ir/train/Operations.Include.h | 29 + runtime/onert/core/include/ir/train/Operations.lst | 28 + .../onert/core/include/ir/train/TrainableGraph.h | 145 + .../include/ir/train/TrainableOperationVisitor.h | 43 + .../onert/core/include/ir/train/operation/Conv2D.h | 51 + .../ir/train/operation/ElementwiseActivation.h | 52 + .../include/ir/train/operation/FullyConnected.h | 51 + .../onert/core/include/ir/train/operation/Loss.h | 51 + .../core/include/ir/train/operation/Permute.h | 51 + .../onert/core/include/ir/train/operation/Pool2D.h | 51 + .../core/include/ir/train/operation/Reshape.h | 51 + .../core/include/ir/train/operation/Softmax.h | 51 + .../ir/train/operation/UntrainableOperation.h | 63 + runtime/onert/core/include/odc/IQuantizer.h | 36 + runtime/onert/core/include/odc/QuantizeManager.h | 81 + runtime/onert/core/include/util/Config.lst | 1 + runtime/onert/core/include/util/MinMaxMap.h | 47 + runtime/onert/core/include/util/Set.h | 16 +- runtime/onert/core/include/util/Utils.h | 57 +- runtime/onert/core/src/backend/BackendContext.cc | 2 - .../core/src/backend/basic/StaticTensorManager.cc | 9 + runtime/onert/core/src/backend/basic/Tensor.cc | 2 + .../onert/core/src/backend/basic/TensorBuilder.cc | 8 + .../src/backend/basic/train/TrainableTensor.cc | 49 + runtime/onert/core/src/backend/builtin/Backend.h | 28 + .../core/src/backend/builtin/BackendContext.cc | 2 +- runtime/onert/core/src/backend/builtin/Config.cc | 2 +- runtime/onert/core/src/backend/builtin/Config.h | 2 +- .../core/src/backend/builtin/KernelGenerator.cc | 8 +- .../core/src/backend/builtin/kernel/WhileLayer.cc | 4 +- .../src/backend/builtin/train/BackendContext.cc | 78 + .../src/backend/builtin/train/BackendContext.h | 76 + .../src/backend/builtin/train/KernelGenerator.cc | 98 + .../src/backend/builtin/train/KernelGenerator.h | 75 + .../onert/core/src/backend/builtin/train/Tensor.h | 40 + .../src/backend/builtin/train/TensorRegistry.h | 132 + .../backend/builtin/train/kernel/PermuteLayer.cc | 85 + .../backend/builtin/train/kernel/PermuteLayer.h | 60 + runtime/onert/core/src/compiler/Compiler.cc | 38 +- runtime/onert/core/src/compiler/CompilerFactory.cc | 15 +- runtime/onert/core/src/compiler/CompilerHelpers.h | 52 + runtime/onert/core/src/compiler/CompilerOptions.cc | 1 + runtime/onert/core/src/compiler/ExecutorFactory.cc | 452 +- runtime/onert/core/src/compiler/ExecutorFactory.h | 66 +- .../onert/core/src/compiler/Fp32ToFp16Converter.cc | 4 +- runtime/onert/core/src/compiler/HEScheduler.cc | 22 +- runtime/onert/core/src/compiler/HEScheduler.h | 8 +- .../onert/core/src/compiler/HEScheduler.test.cc | 6 +- runtime/onert/core/src/compiler/Linear.cc | 6 +- runtime/onert/core/src/compiler/Linear.h | 6 +- runtime/onert/core/src/compiler/LoweredGraph.cc | 22 +- runtime/onert/core/src/compiler/ManualScheduler.cc | 6 +- .../onert/core/src/compiler/MultiModelCompiler.cc | 46 +- .../onert/core/src/compiler/MultiModelCompiler.h | 6 - runtime/onert/core/src/compiler/ShapeValidator.cc | 2 +- .../onert/core/src/compiler/StaticShapeInferer.cc | 36 +- .../src/compiler/pass/ConstantInsertionPass.cc | 4 +- .../core/src/compiler/pass/ConstantInsertionPass.h | 2 +- .../core/src/compiler/pass/ConstantLoweringPass.cc | 4 +- .../core/src/compiler/pass/ConstantLoweringPass.h | 2 +- .../core/src/compiler/pass/ConstantOutputPass.cc | 2 +- runtime/onert/core/src/compiler/pass/IPass.h | 41 + .../core/src/compiler/pass/LoweredOperandPass.h | 6 +- .../core/src/compiler/pass/LoweredOperationPass.h | 8 +- .../onert/core/src/compiler/pass/OperationPass.cc | 4 +- .../onert/core/src/compiler/pass/OperationPass.h | 4 +- runtime/onert/core/src/compiler/pass/Pass.h | 4 +- runtime/onert/core/src/compiler/pass/PassRunner.cc | 2 +- runtime/onert/core/src/compiler/pass/PassRunner.h | 6 +- .../compiler/pass/PermutationEliminationPass.cc | 9 +- .../src/compiler/pass/PermutationEliminationPass.h | 2 +- .../src/compiler/pass/PermutationInsertionPass.cc | 6 +- .../src/compiler/pass/PermutationOperationPass.cc | 2 +- .../src/compiler/pass/PermutationOperationPass.h | 2 +- .../compiler/pass/UnusedOperandEliminationPass.cc | 6 +- .../src/compiler/train/LoweredTrainableGraph.cc | 285 + .../compiler/train/StaticDerivativeShapeInferer.cc | 150 + .../compiler/train/StaticDerivativeShapeInferer.h | 80 + .../core/src/compiler/train/TensorRegistries.h | 105 + .../compiler/train/TrainableOperationConverter.cc | 86 + .../compiler/train/TrainableOperationConverter.h | 57 + .../core/src/compiler/train/TrainingCompiler.cc | 299 + .../core/src/compiler/train/TrainingCompiler.h | 83 + .../train/UntrainableOperationConverter.cc | 53 + .../compiler/train/UntrainableOperationConverter.h | 52 + .../src/compiler/train/pass/LossInsertionPass.cc | 77 + .../src/compiler/train/pass/LossInsertionPass.h | 55 + runtime/onert/core/src/compiler/train/pass/Pass.h | 64 + runtime/onert/core/src/dumper/dot/DotBuilder.cc | 4 +- runtime/onert/core/src/dumper/dot/DotDumper.cc | 15 +- runtime/onert/core/src/dumper/dot/DotDumper.h | 4 +- runtime/onert/core/src/dumper/dot/OperationNode.cc | 2 +- runtime/onert/core/src/dumper/dot/OperationNode.h | 4 +- runtime/onert/core/src/dumper/h5/Dumper.cc | 34 + runtime/onert/core/src/dumper/h5/Dumper.h | 51 + runtime/onert/core/src/dumper/h5/MinMaxDumper.cc | 75 + runtime/onert/core/src/dumper/h5/MinMaxDumper.h | 70 + runtime/onert/core/src/dumper/text/GraphDumper.cc | 28 +- runtime/onert/core/src/dumper/text/GraphDumper.h | 15 +- runtime/onert/core/src/exec/DataflowExecutor.cc | 10 +- runtime/onert/core/src/exec/DynamicShapeInferer.cc | 8 +- runtime/onert/core/src/exec/ExecTime.test.cc | 2 +- runtime/onert/core/src/exec/Execution.cc | 42 + runtime/onert/core/src/exec/ExecutionObservers.cc | 2 +- runtime/onert/core/src/exec/ExecutionObservers.h | 2 +- runtime/onert/core/src/exec/ExecutorBase.cc | 2 +- runtime/onert/core/src/exec/ExecutorBase.h | 1 + runtime/onert/core/src/exec/Executors.cc | 2 +- runtime/onert/core/src/exec/FunctionSequence.cc | 1 - runtime/onert/core/src/exec/LinearExecutor.h | 2 +- runtime/onert/core/src/exec/MinMaxRecorder.cc | 112 + runtime/onert/core/src/exec/MinMaxRecorder.h | 56 + runtime/onert/core/src/exec/ParallelScheduler.cc | 2 +- .../onert/core/src/exec/train/TrainableExecutor.cc | 204 + .../onert/core/src/exec/train/TrainableExecutor.h | 109 + .../core/src/exec/train/TrainableExecutors.cc | 89 + .../onert/core/src/exec/train/TrainableExecutors.h | 92 + .../core/src/exec/train/TrainableFnSequence.cc | 67 + .../core/src/exec/train/optimizer/OptimizerCode.cc | 42 + .../src/exec/train/optimizer/OptimizerHelpers.h | 47 + runtime/onert/core/src/exec/train/optimizer/SGD.cc | 66 + runtime/onert/core/src/ir/Graph.cc | 59 +- runtime/onert/core/src/ir/LayoutSet.cc | 8 +- runtime/onert/core/src/ir/LayoutSet.h | 1 + runtime/onert/core/src/ir/OperandIndexSequence.cc | 9 +- runtime/onert/core/src/ir/OperationCloner.cc | 2 +- runtime/onert/core/src/ir/OperationCloner.h | 2 +- runtime/onert/core/src/ir/OperationDumper.cc | 8 + runtime/onert/core/src/ir/OperationDumper.h | 1 + runtime/onert/core/src/ir/OperationValidator.cc | 6 +- runtime/onert/core/src/ir/Operations.cc | 2 +- runtime/onert/core/src/ir/operation/Loss.cc | 52 + runtime/onert/core/src/ir/train/TrainableGraph.cc | 145 + .../onert/core/src/ir/train/operation/Conv2D.cc | 49 + .../ir/train/operation/ElementwiseActivation.cc | 49 + .../core/src/ir/train/operation/FullyConnected.cc | 49 + runtime/onert/core/src/ir/train/operation/Loss.cc | 48 + .../onert/core/src/ir/train/operation/Permute.cc | 50 + .../onert/core/src/ir/train/operation/Pool2D.cc | 49 + .../onert/core/src/ir/train/operation/Reshape.cc | 49 + .../onert/core/src/ir/train/operation/Softmax.cc | 49 + runtime/onert/core/src/ir/verifier/Verifier.cc | 16 +- runtime/onert/core/src/odc/QuantizeManager.cc | 50 + runtime/onert/core/src/odc/QuantizeManager.test.cc | 36 + runtime/onert/core/src/odc/QuantizerLoader.cc | 104 + runtime/onert/core/src/odc/QuantizerLoader.h | 89 + runtime/onert/core/src/odc/QuantizerLoader.test.cc | 63 + runtime/onert/core/src/util/MDTableEventWriter.cc | 4 +- .../frontend/base_loader/include/base_loader.h | 39 +- .../include/circle_schema_generated.h | 1216 +- .../frontend/nnapi/wrapper/ANeuralNetworksModel.cc | 4 +- .../frontend/tflite/src/tflite_schema_generated.h | 569 +- runtime/onert/frontend/tflite/tflite_schema.fbs | 237 +- runtime/onert/odc/CMakeLists.txt | 39 + runtime/onert/odc/Quantizer.cc | 83 + runtime/onert/odc/Quantizer.h | 39 + runtime/onert/odc/Quantizer.test.cc | 42 + runtime/service/npud/backend/trix/TrixBackend.cc | 4 +- runtime/service/npud/core/Backend.h | 1 + tests/nnfw_api/src/CircleGen.cc | 2 +- tests/nnfw_api/src/one_op_tests/Conv2D.test.cc | 51 + .../src/one_op_tests/DepthwiseConv2D.test.cc | 65 + tests/tools/onert_run/src/args.cc | 4 + tests/tools/onert_run/src/args.h | 4 + tests/tools/onert_run/src/onert_run.cc | 59 + tests/tools/onert_train/CMakeLists.txt | 60 + tests/tools/onert_train/README.md | 13 + tests/tools/onert_train/src/allocation.h | 46 + tests/tools/onert_train/src/args.cc | 291 + tests/tools/onert_train/src/args.h | 92 + tests/tools/onert_train/src/formatter.h | 47 + tests/tools/onert_train/src/h5formatter.cc | 258 + tests/tools/onert_train/src/h5formatter.h | 41 + tests/tools/onert_train/src/measure.h | 90 + tests/tools/onert_train/src/nnfw_util.cc | 49 + tests/tools/onert_train/src/nnfw_util.h | 37 + tests/tools/onert_train/src/onert_train.cc | 277 + tests/tools/onert_train/src/randomgen.cc | 77 + tests/tools/onert_train/src/randomgen.h | 40 + tests/tools/onert_train/src/rawdataloader.cc | 77 + tests/tools/onert_train/src/rawdataloader.h | 51 + tests/tools/onert_train/src/rawformatter.cc | 97 + tests/tools/onert_train/src/rawformatter.h | 40 + .../tools/onert_train/src/types.h | 16 +- tests/tools/onert_train/test/rawdataloader.test.cc | 224 + tools/cross/aarch64/sources.list.jammy | 11 + tools/cross/aarch64/sources.list.xenial | 11 - tools/cross/armel/tizen-build-rootfs.sh | 43 - tools/cross/armel/tizen-fetch.sh | 169 - tools/cross/armel/tizen.patch | 18 - tools/cross/install_rootfs.sh | 29 +- .../tf_dataset_converter/README.md | 66 + .../tf_dataset_converter/argparser.py | 54 + .../tf_dataset_converter/datasets.py | 80 + .../generate_datafile/tf_dataset_converter/main.py | 98 + .../tf_dataset_converter/requirements.txt | 8 + 1445 files changed, 103587 insertions(+), 25576 deletions(-) create mode 100644 .github/workflows/run-onert-micro-unit-tests.yml delete mode 100644 compiler/circle-mpqsolver/src/bisection/ErrorApproximator.cpp delete mode 100644 compiler/circle-mpqsolver/src/bisection/ErrorApproximator.test.cpp create mode 100644 compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.test.cpp create mode 100644 compiler/circle-mpqsolver/src/core/Dumper.cpp create mode 100644 compiler/circle-mpqsolver/src/core/Dumper.h create mode 100644 compiler/circle-mpqsolver/src/core/DumpingHooks.cpp create mode 100644 compiler/circle-mpqsolver/src/core/DumpingHooks.h rename compiler/circle-mpqsolver/src/{bisection => core}/ErrorMetric.cpp (94%) rename compiler/circle-mpqsolver/src/{bisection => core}/ErrorMetric.h (87%) create mode 100644 compiler/circle-mpqsolver/src/core/ErrorMetric.test.cpp rename compiler/circle-mpqsolver/src/{bisection => core}/Evaluator.cpp (97%) rename compiler/circle-mpqsolver/src/{bisection => core}/Evaluator.h (89%) rename compiler/circle-mpqsolver/src/{bisection => core}/Quantizer.cpp (95%) rename compiler/circle-mpqsolver/src/{bisection => core}/Quantizer.h (76%) rename compiler/circle-mpqsolver/src/{bisection => core}/Quantizer.test.cpp (92%) rename onert-micro/luci-interpreter/pal/linux/PALFill.h => compiler/circle-mpqsolver/src/core/SolverHooks.cpp (64%) create mode 100644 compiler/circle-mpqsolver/src/core/SolverHooks.h create mode 100644 compiler/circle-mpqsolver/src/core/SolverOutput.cpp create mode 100644 compiler/circle-mpqsolver/src/core/SolverOutput.h rename compiler/circle-mpqsolver/src/{bisection => core}/TestHelper.h (100%) rename onert-micro/luci-interpreter/pal/linux/PALElu.h => compiler/luci-interpreter/pal/linux/PALGelu.h (53%) rename onert-micro/luci-interpreter/pal/linux/PALLeakyRelu.h => compiler/luci-interpreter/pal/linux/PALHardSwish.h (63%) create mode 100644 compiler/luci-interpreter/src/kernels/FloorMod.cpp rename onert-micro/luci-interpreter/src/kernels/Less.h => compiler/luci-interpreter/src/kernels/FloorMod.h (68%) create mode 100644 compiler/luci-interpreter/src/kernels/FloorMod.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Gelu.cpp rename onert-micro/luci-interpreter/src/kernels/Exp.h => compiler/luci-interpreter/src/kernels/Gelu.h (73%) create mode 100644 compiler/luci-interpreter/src/kernels/Gelu.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/HardSwish.cpp rename onert-micro/luci-interpreter/src/kernels/Elu.h => compiler/luci-interpreter/src/kernels/HardSwish.h (74%) create mode 100644 compiler/luci-interpreter/src/kernels/HardSwish.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Log.cpp rename onert-micro/luci-interpreter/src/kernels/Relu6.h => compiler/luci-interpreter/src/kernels/Log.h (73%) create mode 100644 compiler/luci-interpreter/src/kernels/Log.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Select.cpp rename onert-micro/luci-interpreter/src/kernels/Greater.h => compiler/luci-interpreter/src/kernels/Select.h (54%) create mode 100644 compiler/luci-interpreter/src/kernels/Select.test.cpp create mode 100644 compiler/luci-interpreter/src/kernels/Sum.cpp rename onert-micro/luci-interpreter/src/kernels/Shape.h => compiler/luci-interpreter/src/kernels/Sum.h (63%) create mode 100644 compiler/luci-interpreter/src/kernels/Sum.test.cpp rename onert-micro/luci-interpreter/src/loader/nodes/Floor.cpp => compiler/luci-interpreter/src/loader/nodes/FloorMod.cpp (52%) rename onert-micro/luci-interpreter/src/loader/nodes/PRelu.cpp => compiler/luci-interpreter/src/loader/nodes/Gelu.cpp (52%) rename onert-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp => compiler/luci-interpreter/src/loader/nodes/HardSwish.cpp (56%) rename onert-micro/luci-interpreter/src/loader/nodes/Exp.cpp => compiler/luci-interpreter/src/loader/nodes/Log.cpp (56%) rename onert-micro/luci-interpreter/src/loader/nodes/Cast.cpp => compiler/luci-interpreter/src/loader/nodes/Select.cpp (50%) create mode 100644 compiler/luci-interpreter/src/loader/nodes/Sum.cpp create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleGelu.h create mode 100644 compiler/luci/import/include/luci/Import/Nodes/CircleHardSwish.h create mode 100644 compiler/luci/import/src/Nodes/CircleGelu.cpp create mode 100644 compiler/luci/import/src/Nodes/CircleHardSwish.cpp create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleGelu.h create mode 100644 compiler/luci/lang/include/luci/IR/Nodes/CircleHardSwish.h create mode 100644 compiler/luci/lang/src/Nodes/CircleGelu.test.cpp create mode 100644 compiler/luci/lang/src/Nodes/CircleHardSwish.test.cpp rename onert-micro/luci-interpreter/src/loader/nodes/While.cpp => compiler/luci/partition/src/Nodes/CircleGelu.cpp (53%) create mode 100644 compiler/luci/partition/src/Nodes/CircleGelu.test.cpp create mode 100644 compiler/luci/partition/src/Nodes/CircleHardSwish.cpp create mode 100644 compiler/luci/partition/src/Nodes/CircleHardSwish.test.cpp create mode 100644 compiler/luci/pass/include/luci/DynamicBatchToSingleBatch.h rename onert-micro/luci-interpreter/src/loader/nodes/If.cpp => compiler/luci/pass/include/luci/Pass/DecomposeHardSwishPass.h (53%) create mode 100644 compiler/luci/pass/include/luci/Pass/DynamicBatchToSingleBatchPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/FuseGeluPass.h create mode 100644 compiler/luci/pass/include/luci/Pass/QuantizeWeightsPass.h create mode 100644 compiler/luci/pass/src/DecomposeHardSwishPass.cpp create mode 100644 compiler/luci/pass/src/DecomposeHardSwishPass.test.cpp create mode 100644 compiler/luci/pass/src/DynamicBatchToSingleBatch.cpp create mode 100644 compiler/luci/pass/src/DynamicBatchToSingleBatchPass.cpp create mode 100644 compiler/luci/pass/src/DynamicBatchToSingleBatchPass.test.cpp create mode 100644 compiler/luci/pass/src/FuseGeluPass.cpp create mode 100644 compiler/luci/pass/src/FuseGeluPass.test.cpp create mode 100644 compiler/luci/pass/src/QuantizeWeightsOnly.cpp create mode 100644 compiler/luci/pass/src/QuantizeWeightsOnly.h create mode 100644 compiler/luci/pass/src/QuantizeWeightsPass.cpp create mode 100644 compiler/luci/pass/src/QuantizeWeightsPass.test.cpp rename onert-micro/luci-interpreter/pal/cmsisnn/PALFill.h => compiler/luci/pass/src/helpers/CreateCircleConst.cpp (68%) create mode 100644 compiler/luci/pass/src/helpers/CreateCircleConst.h create mode 100644 compiler/luci/service/src/Nodes/CircleGelu.cpp create mode 100644 compiler/luci/service/src/Nodes/CircleGelu.test.cpp create mode 100644 compiler/luci/service/src/Nodes/CircleHardSwish.cpp create mode 100644 compiler/luci/service/src/Nodes/CircleHardSwish.test.cpp create mode 100644 compiler/mio-circle05/CMakeLists.txt create mode 100644 compiler/mio-circle05/README.md create mode 100644 compiler/mio-circle05/example.cpp create mode 100644 compiler/mio-circle05/include/mio_circle/Helper.h create mode 100644 compiler/mio-circle05/include/mio_circle/Reader.h create mode 100644 compiler/mio-circle05/src/Helper.cpp create mode 100644 compiler/mio-circle05/src/Helper.test.cpp create mode 100644 compiler/mio-circle05/src/Reader.cpp create mode 100644 compiler/mio-circle05/src/Reader.test.cpp create mode 100644 compiler/mio-circle06/CMakeLists.txt create mode 100644 compiler/mio-circle06/README.md create mode 100644 compiler/mio-circle06/example.cpp create mode 100644 compiler/mio-circle06/include/mio_circle/Helper.h create mode 100644 compiler/mio-circle06/include/mio_circle/Reader.h create mode 100644 compiler/mio-circle06/src/Helper.cpp create mode 100644 compiler/mio-circle06/src/Helper.test.cpp create mode 100644 compiler/mio-circle06/src/Reader.cpp create mode 100644 compiler/mio-circle06/src/Reader.test.cpp create mode 100644 compiler/mio-tflite2121/CMakeLists.txt create mode 100644 compiler/mio-tflite2121/README.md create mode 100644 compiler/mio-tflite2121/example.cpp create mode 100644 compiler/mio-tflite2121/include/mio_tflite2121/Helper.h create mode 100644 compiler/mio-tflite2121/src/Helper.cpp create mode 100644 compiler/mio-tflite2121/src/Helper.test.cpp rename compiler/{circle-mpqsolver/src/bisection/ErrorApproximator.h => one-cmds/dummy-driver/src/dummy-onnx-ext.cpp} (59%) create mode 100644 compiler/one-cmds/dummy-driver/src/dummyV2-compile.cpp create mode 100644 compiler/one-cmds/dummy-driver/src/dummyV2-profile.cpp create mode 100644 compiler/one-cmds/dummy-driver/src/dummyV3-profile.cpp create mode 100644 compiler/one-cmds/one-prepare-venv.aarch64 rename compiler/one-cmds/{one-prepare-venv.u2204 => one-prepare-venv.u1804} (90%) create mode 100644 compiler/one-cmds/onelib/backends.py create mode 100644 compiler/one-cmds/tests/one-codegen_neg_002.test create mode 100644 compiler/one-cmds/tests/one-codegen_neg_003.test create mode 100644 compiler/one-cmds/tests/one-codegen_neg_004.cfg create mode 100644 compiler/one-cmds/tests/one-codegen_neg_004.test create mode 100644 compiler/one-cmds/tests/one-codegen_neg_005.test create mode 100644 compiler/one-cmds/tests/one-import-onnx_ext_001.test create mode 100644 compiler/one-cmds/tests/one-profile_neg_002.test create mode 100644 compiler/one-cmds/tests/one-profile_neg_003.test create mode 100644 compiler/one-cmds/tests/one-profile_neg_004.cfg create mode 100644 compiler/one-cmds/tests/one-profile_neg_004.test create mode 100644 compiler/one-cmds/tests/one-profile_neg_005.test create mode 100644 compiler/one-cmds/tests/one-quantize_017.test create mode 100644 compiler/one-cmds/tests/one-quantize_018.test create mode 100644 compiler/one-cmds/tests/one-quantize_019.test create mode 100644 compiler/one-cmds/tests/one-quantize_020.test create mode 100644 compiler/one-cmds/tests/one-quantize_021.test create mode 100644 compiler/one-cmds/tests/one-quantize_neg_022.test create mode 100644 compiler/one-cmds/tests/one-quantize_neg_023.test create mode 100644 compiler/one-cmds/tests/onecc_046.cfg create mode 100644 compiler/one-cmds/tests/onecc_046.test create mode 100644 compiler/one-cmds/tests/onecc_047.cfg create mode 100644 compiler/one-cmds/tests/onecc_047.test create mode 100644 compiler/one-cmds/tests/onecc_048.cfg create mode 100644 compiler/one-cmds/tests/onecc_048.test create mode 100644 compiler/one-cmds/tests/onecc_049.cfg create mode 100644 compiler/one-cmds/tests/onecc_049.test create mode 100644 compiler/one-cmds/tests/onecc_050.cfg create mode 100644 compiler/one-cmds/tests/onecc_050.test create mode 100644 compiler/one-cmds/tests/onecc_051.cfg create mode 100644 compiler/one-cmds/tests/onecc_051.test create mode 100644 compiler/one-cmds/tests/onecc_052.cfg create mode 100644 compiler/one-cmds/tests/onecc_052.test create mode 100644 compiler/one-cmds/tests/onecc_053.cfg create mode 100644 compiler/one-cmds/tests/onecc_053.test create mode 100644 compiler/one-cmds/tests/onecc_054.cfg create mode 100644 compiler/one-cmds/tests/onecc_054.test create mode 100644 compiler/one-cmds/tests/onecc_055.cfg create mode 100644 compiler/one-cmds/tests/onecc_055.test create mode 100644 compiler/one-cmds/tests/onecc_056.cfg create mode 100644 compiler/one-cmds/tests/onecc_056.test create mode 100644 compiler/one-cmds/tests/onecc_057.cfg create mode 100644 compiler/one-cmds/tests/onecc_057.test create mode 100644 compiler/one-cmds/tests/onecc_058.cfg create mode 100644 compiler/one-cmds/tests/onecc_058.test create mode 100644 compiler/one-cmds/tests/onecc_neg_027.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_027.test create mode 100644 compiler/one-cmds/tests/onecc_neg_028.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_028.test create mode 100644 compiler/one-cmds/tests/onecc_neg_029.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_029.test create mode 100644 compiler/one-cmds/tests/onecc_neg_030.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_030.test create mode 100644 compiler/one-cmds/tests/onecc_neg_031.test create mode 100644 compiler/one-cmds/tests/onecc_neg_031.workflow.json create mode 100644 compiler/one-cmds/tests/onecc_neg_032.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_032.test create mode 100644 compiler/one-cmds/tests/onecc_neg_033.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_033.test create mode 100644 compiler/one-cmds/tests/onecc_neg_034.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_034.test create mode 100644 compiler/one-cmds/tests/onecc_neg_035.cfg create mode 100644 compiler/one-cmds/tests/onecc_neg_035.test create mode 100644 compiler/one-cmds/tests/onecc_neg_036.test create mode 100644 compiler/one-cmds/tests/onecc_neg_036.workflow.json create mode 100644 compiler/oops/requires.cmake create mode 100644 compiler/pics/requires.cmake create mode 100644 compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/wo_quantization/ker.json create mode 100644 compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int8/wo_quantization/ker.json create mode 100644 compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/wo_quantization/ker.json create mode 100644 compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int8/wo_quantization/ker.json create mode 100755 compiler/pota-quantization-value-test/test_wo_quantization.sh create mode 100644 compiler/record-minmax/include/MinMaxComputer.h create mode 100644 compiler/record-minmax/include/MinMaxVectors.h create mode 100644 compiler/record-minmax/src/MinMaxComputer.cpp create mode 100644 compiler/record-minmax/src/RecordFunction.cpp create mode 100644 compiler/record-minmax/tests/MinMaxComputer.test.cpp create mode 100644 compiler/tflchef/core/src/CustomOp/Erf.cpp create mode 100644 compiler/tflchef/core/src/CustomOp/Erf.h create mode 100644 compiler/tflchef/core/src/Op/Gelu.cpp create mode 100644 compiler/tflchef/core/src/Op/Gelu.h create mode 100644 compiler/tflchef/core/src/Op/HardSwish.cpp create mode 100644 compiler/tflchef/core/src/Op/HardSwish.h create mode 100644 compiler/tflchef/tests/custom_erf/test.recipe create mode 100644 compiler/tflchef/tflite/src/Op/Gelu.cpp create mode 100644 compiler/tflchef/tflite/src/Op/HardSwish.cpp create mode 100644 compiler/tflchef/tflite/src/Op/include/Gelu.h create mode 100644 compiler/tflchef/tflite/src/Op/include/HardSwish.h create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.cpp create mode 100644 compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.h create mode 100644 compute/cker/include/cker/eigen/eigen_gemm_eigen.h create mode 100644 compute/cker/include/cker/operation/optimized/Gemm.h create mode 100644 compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvHybrid.h create mode 100644 compute/cker/include/cker/train/operation/FullyConnected.h create mode 100644 compute/cker/include/cker/train/operation/Loss.h create mode 100644 compute/cker/include/cker/train/operation/ReLU.h create mode 100644 compute/cker/src/train/FullyConnected.test.cc create mode 100644 compute/cker/src/train/Loss.test.cc create mode 100644 compute/cker/src/train/Relu.test.cc create mode 100644 docs/release/1.23/index.rst create mode 100644 docs/release/1.23/release-note-1.23.0.md create mode 100644 docs/release/1.24/index.rst create mode 100644 docs/release/1.24/release-note-1.24.0.md create mode 100644 docs/release/1.25/index.rst create mode 100644 docs/release/1.25/release-note_1.25.0.md create mode 100644 docs/release/onert-micro/0.1/release-note-0.1.0.md create mode 100644 docs/release/onert-micro/1.0/release-note-1.0.0.md create mode 100644 infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfig.cmake create mode 100644 infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfigVersion.cmake create mode 100644 infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfig.cmake create mode 100644 infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfigVersion.cmake create mode 100644 infra/docker/bionic/Dockerfile.aarch64 create mode 100644 infra/docker/focal/Dockerfile.aarch64 create mode 100644 infra/docker/jammy/Dockerfile.aarch64 create mode 100644 infra/nncc/cmake/buildtool/config/config_aarch64-linux.cmake create mode 100644 infra/nncc/cmake/buildtool/config/config_aarch64-tizen.cmake create mode 100644 infra/nncc/cmake/buildtool/config/config_armv7hl-tizen.cmake create mode 100644 infra/nncc/cmake/buildtool/config/config_armv7l-tizen.cmake create mode 100644 infra/nncc/cmake/buildtool/config/config_i686-tizen.cmake create mode 100644 infra/nncc/cmake/buildtool/config/config_x86_64-tizen.cmake create mode 100644 infra/nncc/cmake/options/options_aarch64-darwin.cmake create mode 100644 infra/nncc/cmake/options/options_aarch64-linux.cmake create mode 100644 infra/nncc/cmake/options/options_aarch64-tizen.cmake create mode 100644 infra/nncc/cmake/options/options_armv7hl-tizen.cmake create mode 100644 infra/nncc/cmake/options/options_armv7l-tizen.cmake create mode 100644 infra/nncc/cmake/options/options_i686-tizen.cmake create mode 100644 infra/nncc/cmake/options/options_x86_64-darwin.cmake create mode 100644 infra/nncc/cmake/options/options_x86_64-tizen.cmake delete mode 100644 infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake create mode 100644 infra/nnfw/cmake/packages/LuciConfig.cmake create mode 100644 infra/packaging/preset/20230413 create mode 100644 infra/packaging/preset/20230413_windows create mode 100644 infra/packaging/preset/20230907 create mode 100644 infra/packaging/preset/20230907_windows create mode 100644 infra/packaging/res/tf2nnpkg.20230413 create mode 100644 infra/packaging/res/tf2nnpkg.20230907 create mode 100644 onert-micro/externals/CMakeLists.txt create mode 100644 onert-micro/externals/flatbuffers/base.h create mode 100644 onert-micro/externals/flatbuffers/code_generators.h create mode 100644 onert-micro/externals/flatbuffers/flatbuffers.h create mode 100644 onert-micro/externals/flatbuffers/flatc.h create mode 100644 onert-micro/externals/flatbuffers/flexbuffers.h create mode 100644 onert-micro/externals/flatbuffers/grpc.h create mode 100644 onert-micro/externals/flatbuffers/hash.h create mode 100644 onert-micro/externals/flatbuffers/idl.h create mode 100644 onert-micro/externals/flatbuffers/minireflect.h create mode 100644 onert-micro/externals/flatbuffers/pch/flatc_pch.h create mode 100644 onert-micro/externals/flatbuffers/pch/pch.h create mode 100644 onert-micro/externals/flatbuffers/reflection.h create mode 100644 onert-micro/externals/flatbuffers/reflection_generated.h create mode 100644 onert-micro/externals/flatbuffers/registry.h create mode 100644 onert-micro/externals/flatbuffers/stl_emulation.h create mode 100644 onert-micro/externals/flatbuffers/util.h create mode 100644 onert-micro/externals/gen/circle-generated/circle/schema_generated.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/TrainingSettings.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/onert-micro-version.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/TestDataBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/FloatAbsKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/NegAbsKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/TestDataAbsBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/FloatAddKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/IntAddKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/NegAddKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/TestDataAddBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/FloatAddNKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/NegAddNKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/TestDataAddNBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/FloatArgMaxKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/NegArgMaxKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/TestDataArgMaxBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/FloatArgMinKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/NegArgMinKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/TestDataArgMinBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/FloatAveragePool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/NegAveragePool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/TestDataAveragePool2DBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/FloatConcatenationKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/IntConcatenationKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/NegConcatenationKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/TestDataConcatenationBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/FloatConv2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/NegConv2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/TestDataConv2DBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/U8Conv2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/FloatDivKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/NegDivKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/TestDataDivBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/FloatEluKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/NegEluKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/TestDataEluBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/FloatEqualKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/IntEqualKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/TestDataEqualBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/FloatExpKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/NegExpKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/TestDataExpBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/expand_dims/ExpandDimsKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/FillKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/NegFillKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/FloatFullyConnectedKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/NegFullyConnectedKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/TestDataFullyConnectedBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/U8FullyConnectedKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/FloatGatherKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/IntGatherKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/NegGatherKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/TestDataGatherBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/FloatGreaterKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/TestDataGreaterBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/FloatGreaterEqualKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/TestDataGreaterEqualBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/FloatLeakyReLUKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/NegLeakyReLUKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/TestDataLeakyReLUBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/FloatLessKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/IntLessKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/NegTestDataLessKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/QuantLessKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/TestDataLessBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/FloatLessEqualKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/TestDataLessEqualBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/BoolLogicalAndKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/NegLogicalAndKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/TestDataLogicalAndBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/BoolLogicalOrKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/NegLogicalOrKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/TestDataLogicalOrBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/FloatLogisticKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/NegLogisticKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/TestDataLogisticBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/FloatMaxPool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/NegMaxPool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/TestDataMaxPool2DBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/FloatMulKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/IntMulKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/NegMulKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/TestDataMulBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/FloatNegKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/NegNegKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/TestDataNegBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/FloatNotEqualKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/TestDataNotEqualBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/PackKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/TestDataPackBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/FloatPadKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/NegPadKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/TestDataPadBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/FloatPadV2Kernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/NegPadV2Kernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/TestDataPadV2Base.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/NegReduceProdKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/ReduceProdKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/TestDataReduceCommonBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/FloatReLUKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/NegReLUKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/TestDataReLUBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/FloatReLU6Kernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/NegReLU6Kernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/TestDataReLU6Base.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/reshape/ReshapeKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/FloatResizeBilinearKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/NegResizeBilinearKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/TestDataResizeBilinearBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/U8ResizeBilinearKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/NegShapeKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/ShapeKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/FloatSliceKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/NegSliceKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantS16SliceKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantU8SliceKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/TestDataSliceBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/FloatSplitKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/IntSplitKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/TestDataSplitBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/split_v/SplitVKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/strided_slice/StridedSliceKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/FloatSubKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/IntSubKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/NegSubKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/TestDataSubBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/FloatTanhKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/NegTanhKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/TestDataTanhBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/transpose/TransposeKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/FloatUnidirectionalLSTMKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/QuantS8UnidirectionalLSTM.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/TestDataUnidirectionalLSTMBase.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/NegWhileKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/WhileKernel.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h delete mode 100644 onert-micro/luci-interpreter/pal/cmsisnn/PALreference_ops.h rename onert-micro/luci-interpreter/pal/{cmsisnn/PALElu.h => common/PALAbs.h} (54%) create mode 100644 onert-micro/luci-interpreter/pal/common/PALAddCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALAddN.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALArgMinMax.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALAveragePool2DCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALComparisons.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALConcatenation.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALConv2DCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALDiv.h rename onert-micro/luci-interpreter/pal/{mcu => common}/PALElu.h (63%) rename onert-micro/luci-interpreter/pal/{cmsisnn/PALNeg.h => common/PALExp.h} (53%) create mode 100644 onert-micro/luci-interpreter/pal/common/PALFullyConnectedCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALLogicalCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALLogistic.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALMaxPool2DCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALMulCommon.h rename onert-micro/luci-interpreter/pal/{linux => common}/PALNeg.h (53%) create mode 100644 onert-micro/luci-interpreter/pal/common/PALPad.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALReduceCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALReluCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALResizeBilinear.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALSoftmax.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALStridedSlice.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALSub.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALTanh.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALTranspose.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALUnidirectionalSequenceLSTMCommon.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALUtils.h create mode 100644 onert-micro/luci-interpreter/pal/common/Params.h create mode 100644 onert-micro/luci-interpreter/pal/common/ProcessBroadcastShapes.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/KernelsToBuild.lst delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALArgMax.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALAveragePool2d.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALBatchMatMul.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALConv2d.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALDepthToSpace.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALDequantize.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALFullyConnected.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALGather.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALL2Normalize.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALL2Pool2D.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALLogSoftmax.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALMul.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALQuantize.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALRelu.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALRelu6.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALResizeBilinear.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSVDF.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSlice.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSoftmax.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSplit.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/PALSub.h delete mode 100644 onert-micro/luci-interpreter/pal/linux/pal.cmake create mode 100644 onert-micro/luci-interpreter/pal/mcu/PALAdd.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALApplyActivationToVector.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALArgMax.h create mode 100644 onert-micro/luci-interpreter/pal/mcu/PALAveragePool2D.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALDequantize.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALL2Normalize.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h create mode 100644 onert-micro/luci-interpreter/pal/mcu/PALMaxPool2D.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALNeg.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALQuantize.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALSVDF.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALSoftmax.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALSub.h delete mode 100644 onert-micro/luci-interpreter/pal/mcu/PALreference_ops.h create mode 100644 onert-micro/luci-interpreter/src/kernels/Abs.cpp create mode 100644 onert-micro/luci-interpreter/src/kernels/Abs.test.cpp delete mode 100644 onert-micro/luci-interpreter/src/kernels/Add.h create mode 100644 onert-micro/luci-interpreter/src/kernels/AddN.cpp create mode 100644 onert-micro/luci-interpreter/src/kernels/AddN.test.cpp delete mode 100644 onert-micro/luci-interpreter/src/kernels/ArgMax.h create mode 100644 onert-micro/luci-interpreter/src/kernels/ArgMin.cpp create mode 100644 onert-micro/luci-interpreter/src/kernels/ArgMin.test.cpp delete mode 100644 onert-micro/luci-interpreter/src/kernels/AveragePool2D.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Div.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Equal.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Fill.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Gather.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/GreaterEqual.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/LeakyRelu.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/LessEqual.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/LogicalAnd.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/LogicalNot.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/LogicalOr.h create mode 100644 onert-micro/luci-interpreter/src/kernels/MISOKernel.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Mul.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Neg.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/NotEqual.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Pack.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Pad.h create mode 100644 onert-micro/luci-interpreter/src/kernels/PadCommon.cpp rename onert-micro/luci-interpreter/src/{loader/nodes/Neg.cpp => kernels/PadCommon.h} (56%) delete mode 100644 onert-micro/luci-interpreter/src/kernels/PadV2.h create mode 100644 onert-micro/luci-interpreter/src/kernels/ReduceCommon.cpp create mode 100644 onert-micro/luci-interpreter/src/kernels/ReduceCommon.test.cpp delete mode 100644 onert-micro/luci-interpreter/src/kernels/Relu.h create mode 100644 onert-micro/luci-interpreter/src/kernels/SISOKernel.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Slice.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/SplitV.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/StridedSlice.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Sub.h create mode 100644 onert-micro/luci-interpreter/src/kernels/TISOKernel.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Tanh.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/Transpose.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/While.h delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Add.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Div.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Elu.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Equal.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Fill.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Gather.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Greater.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Less.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Logistic.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Maximum.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Mean.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Minimum.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Mul.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/OneHot.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Pack.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Pad.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/PadV2.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Pow.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Quantize.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Relu.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Relu6.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Reshape.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/SVDF.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Shape.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Slice.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Softmax.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Split.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/SplitV.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Square.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Sub.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Tanh.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Transpose.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/UnidirectionalSequenceLSTM.cpp delete mode 100644 onert-micro/luci-interpreter/src/loader/nodes/Unpack.cpp create mode 100644 res/CircleSchema/0.5/circle_schema.fbs create mode 100644 res/CircleSchema/0.6/circle_schema.fbs create mode 100644 res/TensorFlowLiteRecipes/Gelu_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Gelu_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/HardSwish_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/HardSwish_000/test.reverse create mode 100644 res/TensorFlowLiteRecipes/HardSwish_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/HardSwish_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_Gelu_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_Gelu_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_Gelu_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_Gelu_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Add_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_AveragePool2D_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Conv_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_DepthwiseConv2D_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_MaxPool2D_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mean_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_Mul_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_PRelu_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_ReLU_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_I8_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Quant_TransposeConv_I8_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/REGRESS_ONNX_Mul_Mul_000/test.recipe create mode 100644 res/TensorFlowLiteRecipes/REGRESS_ONNX_Mul_Mul_000/test.rule create mode 100644 res/TensorFlowLiteSchema/2.10.1/schema.fbs create mode 100644 res/TensorFlowLiteSchema/2.12.1/schema.fbs create mode 100644 runtime/onert/backend/train/Backend.h create mode 100644 runtime/onert/backend/train/BackendContext.cc create mode 100644 runtime/onert/backend/train/BackendContext.h create mode 100644 runtime/onert/backend/train/CMakeLists.txt create mode 100644 runtime/onert/backend/train/Config.cc create mode 100644 runtime/onert/backend/train/Config.h create mode 100644 runtime/onert/backend/train/ExternalContext.h create mode 100644 runtime/onert/backend/train/KernelGenerator.cc create mode 100644 runtime/onert/backend/train/KernelGenerator.h create mode 100644 runtime/onert/backend/train/MemoryManager.h create mode 100644 runtime/onert/backend/train/Tensor.h create mode 100644 runtime/onert/backend/train/TensorBuilder.cc create mode 100644 runtime/onert/backend/train/TensorBuilder.h create mode 100644 runtime/onert/backend/train/TensorManager.cc create mode 100644 runtime/onert/backend/train/TensorManager.h create mode 100644 runtime/onert/backend/train/TensorRegistry.h create mode 100644 runtime/onert/backend/train/ops/ConvolutionLayer.cc create mode 100644 runtime/onert/backend/train/ops/ConvolutionLayer.h create mode 100644 runtime/onert/backend/train/ops/ElementwiseActivationLayer.cc create mode 100644 runtime/onert/backend/train/ops/ElementwiseActivationLayer.h create mode 100644 runtime/onert/backend/train/ops/FullyConnectedLayer.cc create mode 100644 runtime/onert/backend/train/ops/FullyConnectedLayer.h create mode 100644 runtime/onert/backend/train/ops/GradientApplier.cc create mode 100644 runtime/onert/backend/train/ops/GradientApplier.h create mode 100644 runtime/onert/backend/train/ops/LossLayer.cc create mode 100644 runtime/onert/backend/train/ops/LossLayer.h create mode 100644 runtime/onert/backend/train/ops/OperationUtils.h create mode 100644 runtime/onert/backend/train/ops/PoolLayer.cc create mode 100644 runtime/onert/backend/train/ops/PoolLayer.h create mode 100644 runtime/onert/backend/train/ops/ReshapeLayer.cc create mode 100644 runtime/onert/backend/train/ops/ReshapeLayer.h create mode 100644 runtime/onert/backend/train/train.cc create mode 100644 runtime/onert/core/include/backend/basic/train/TrainableBackendContextHelpers.h create mode 100644 runtime/onert/core/include/backend/basic/train/TrainableTensor.h create mode 100644 runtime/onert/core/include/backend/train/ITensorRegistry.h create mode 100644 runtime/onert/core/include/backend/train/ITrainableBackend.h create mode 100644 runtime/onert/core/include/backend/train/ITrainableTensor.h create mode 100644 runtime/onert/core/include/backend/train/KernelGeneratorBase.h create mode 100644 runtime/onert/core/include/backend/train/TrainableBackendContext.h create mode 100644 runtime/onert/core/include/compiler/ILoweredGraph.h create mode 100644 runtime/onert/core/include/compiler/train/LoweredTrainableGraph.h create mode 100644 runtime/onert/core/include/compiler/train/TrainableCodeMap.h create mode 100644 runtime/onert/core/include/compiler/train/TrainingInfo.h create mode 100644 runtime/onert/core/include/exec/MinMaxMap.h create mode 100644 runtime/onert/core/include/exec/train/IGradientApplier.h create mode 100644 runtime/onert/core/include/exec/train/ITrainableFunction.h create mode 100644 runtime/onert/core/include/exec/train/TrainableFnSequence.h create mode 100644 runtime/onert/core/include/exec/train/optimizer/Optimizer.h create mode 100644 runtime/onert/core/include/exec/train/optimizer/OptimizerCode.h create mode 100644 runtime/onert/core/include/exec/train/optimizer/SGD.h create mode 100644 runtime/onert/core/include/ir/IGraph.h create mode 100644 runtime/onert/core/include/ir/IOperation.h create mode 100644 runtime/onert/core/include/ir/operation/Loss.h create mode 100644 runtime/onert/core/include/ir/train/ITrainableOperation.h create mode 100644 runtime/onert/core/include/ir/train/Operations.Include.h create mode 100644 runtime/onert/core/include/ir/train/Operations.lst create mode 100644 runtime/onert/core/include/ir/train/TrainableGraph.h create mode 100644 runtime/onert/core/include/ir/train/TrainableOperationVisitor.h create mode 100644 runtime/onert/core/include/ir/train/operation/Conv2D.h create mode 100644 runtime/onert/core/include/ir/train/operation/ElementwiseActivation.h create mode 100644 runtime/onert/core/include/ir/train/operation/FullyConnected.h create mode 100644 runtime/onert/core/include/ir/train/operation/Loss.h create mode 100644 runtime/onert/core/include/ir/train/operation/Permute.h create mode 100644 runtime/onert/core/include/ir/train/operation/Pool2D.h create mode 100644 runtime/onert/core/include/ir/train/operation/Reshape.h create mode 100644 runtime/onert/core/include/ir/train/operation/Softmax.h create mode 100644 runtime/onert/core/include/ir/train/operation/UntrainableOperation.h create mode 100644 runtime/onert/core/include/odc/IQuantizer.h create mode 100644 runtime/onert/core/include/odc/QuantizeManager.h create mode 100644 runtime/onert/core/include/util/MinMaxMap.h create mode 100644 runtime/onert/core/src/backend/basic/train/TrainableTensor.cc create mode 100644 runtime/onert/core/src/backend/builtin/train/BackendContext.cc create mode 100644 runtime/onert/core/src/backend/builtin/train/BackendContext.h create mode 100644 runtime/onert/core/src/backend/builtin/train/KernelGenerator.cc create mode 100644 runtime/onert/core/src/backend/builtin/train/KernelGenerator.h create mode 100644 runtime/onert/core/src/backend/builtin/train/Tensor.h create mode 100644 runtime/onert/core/src/backend/builtin/train/TensorRegistry.h create mode 100644 runtime/onert/core/src/backend/builtin/train/kernel/PermuteLayer.cc create mode 100644 runtime/onert/core/src/backend/builtin/train/kernel/PermuteLayer.h create mode 100644 runtime/onert/core/src/compiler/CompilerHelpers.h create mode 100644 runtime/onert/core/src/compiler/pass/IPass.h create mode 100644 runtime/onert/core/src/compiler/train/LoweredTrainableGraph.cc create mode 100644 runtime/onert/core/src/compiler/train/StaticDerivativeShapeInferer.cc create mode 100644 runtime/onert/core/src/compiler/train/StaticDerivativeShapeInferer.h create mode 100644 runtime/onert/core/src/compiler/train/TensorRegistries.h create mode 100644 runtime/onert/core/src/compiler/train/TrainableOperationConverter.cc create mode 100644 runtime/onert/core/src/compiler/train/TrainableOperationConverter.h create mode 100644 runtime/onert/core/src/compiler/train/TrainingCompiler.cc create mode 100644 runtime/onert/core/src/compiler/train/TrainingCompiler.h create mode 100644 runtime/onert/core/src/compiler/train/UntrainableOperationConverter.cc create mode 100644 runtime/onert/core/src/compiler/train/UntrainableOperationConverter.h create mode 100644 runtime/onert/core/src/compiler/train/pass/LossInsertionPass.cc create mode 100644 runtime/onert/core/src/compiler/train/pass/LossInsertionPass.h create mode 100644 runtime/onert/core/src/compiler/train/pass/Pass.h create mode 100644 runtime/onert/core/src/dumper/h5/Dumper.cc create mode 100644 runtime/onert/core/src/dumper/h5/Dumper.h create mode 100644 runtime/onert/core/src/dumper/h5/MinMaxDumper.cc create mode 100644 runtime/onert/core/src/dumper/h5/MinMaxDumper.h create mode 100644 runtime/onert/core/src/exec/MinMaxRecorder.cc create mode 100644 runtime/onert/core/src/exec/MinMaxRecorder.h create mode 100644 runtime/onert/core/src/exec/train/TrainableExecutor.cc create mode 100644 runtime/onert/core/src/exec/train/TrainableExecutor.h create mode 100644 runtime/onert/core/src/exec/train/TrainableExecutors.cc create mode 100644 runtime/onert/core/src/exec/train/TrainableExecutors.h create mode 100644 runtime/onert/core/src/exec/train/TrainableFnSequence.cc create mode 100644 runtime/onert/core/src/exec/train/optimizer/OptimizerCode.cc create mode 100644 runtime/onert/core/src/exec/train/optimizer/OptimizerHelpers.h create mode 100644 runtime/onert/core/src/exec/train/optimizer/SGD.cc create mode 100644 runtime/onert/core/src/ir/operation/Loss.cc create mode 100644 runtime/onert/core/src/ir/train/TrainableGraph.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Conv2D.cc create mode 100644 runtime/onert/core/src/ir/train/operation/ElementwiseActivation.cc create mode 100644 runtime/onert/core/src/ir/train/operation/FullyConnected.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Loss.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Permute.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Pool2D.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Reshape.cc create mode 100644 runtime/onert/core/src/ir/train/operation/Softmax.cc create mode 100644 runtime/onert/core/src/odc/QuantizeManager.cc create mode 100644 runtime/onert/core/src/odc/QuantizeManager.test.cc create mode 100644 runtime/onert/core/src/odc/QuantizerLoader.cc create mode 100644 runtime/onert/core/src/odc/QuantizerLoader.h create mode 100644 runtime/onert/core/src/odc/QuantizerLoader.test.cc create mode 100644 runtime/onert/odc/CMakeLists.txt create mode 100644 runtime/onert/odc/Quantizer.cc create mode 100644 runtime/onert/odc/Quantizer.h create mode 100644 runtime/onert/odc/Quantizer.test.cc create mode 100644 tests/tools/onert_train/CMakeLists.txt create mode 100644 tests/tools/onert_train/README.md create mode 100644 tests/tools/onert_train/src/allocation.h create mode 100644 tests/tools/onert_train/src/args.cc create mode 100644 tests/tools/onert_train/src/args.h create mode 100644 tests/tools/onert_train/src/formatter.h create mode 100644 tests/tools/onert_train/src/h5formatter.cc create mode 100644 tests/tools/onert_train/src/h5formatter.h create mode 100644 tests/tools/onert_train/src/measure.h create mode 100644 tests/tools/onert_train/src/nnfw_util.cc create mode 100644 tests/tools/onert_train/src/nnfw_util.h create mode 100644 tests/tools/onert_train/src/onert_train.cc create mode 100644 tests/tools/onert_train/src/randomgen.cc create mode 100644 tests/tools/onert_train/src/randomgen.h create mode 100644 tests/tools/onert_train/src/rawdataloader.cc create mode 100644 tests/tools/onert_train/src/rawdataloader.h create mode 100644 tests/tools/onert_train/src/rawformatter.cc create mode 100644 tests/tools/onert_train/src/rawformatter.h rename onert-micro/luci-interpreter/pal/mcu/PALFill.h => tests/tools/onert_train/src/types.h (68%) create mode 100644 tests/tools/onert_train/test/rawdataloader.test.cc create mode 100644 tools/cross/aarch64/sources.list.jammy delete mode 100644 tools/cross/aarch64/sources.list.xenial delete mode 100755 tools/cross/armel/tizen-build-rootfs.sh delete mode 100755 tools/cross/armel/tizen-fetch.sh delete mode 100644 tools/cross/armel/tizen.patch create mode 100644 tools/generate_datafile/tf_dataset_converter/README.md create mode 100644 tools/generate_datafile/tf_dataset_converter/argparser.py create mode 100644 tools/generate_datafile/tf_dataset_converter/datasets.py create mode 100644 tools/generate_datafile/tf_dataset_converter/main.py create mode 100644 tools/generate_datafile/tf_dataset_converter/requirements.txt diff --git a/.ahub/sam/exclude.txt b/.ahub/sam/exclude.txt index f16f84f..3c2b71f 100644 --- a/.ahub/sam/exclude.txt +++ b/.ahub/sam/exclude.txt @@ -47,3 +47,6 @@ # Downloaded externals /ONE/externals + +# Intermediate code for runtime build (refer nnfw.spec file's nncc_workspace) +/ONE/build/nncc/ diff --git a/.ahub/tcchecker-tca/config.yaml b/.ahub/tcchecker-tca/config.yaml index 154fbf9..12fbabe 100644 --- a/.ahub/tcchecker-tca/config.yaml +++ b/.ahub/tcchecker-tca/config.yaml @@ -118,7 +118,7 @@ test: - /compiler/luci-eval-driver - /compiler/luci-pass-value-test - /compiler/luci-value-test - - /compiler/mio-circle04 + - /compiler/mio-circle05 - /compiler/mio-tflite - /compiler/mio-tflite260 - /compiler/oops @@ -546,3 +546,89 @@ test: positiveTestCase: - condition: - inverse: negativeTestCase + - name: onert-micro + testCaseLanguage: CPP + testFW: GTEST + testCaseFolder: + - /onert-micro + + testFile: + - extension: test.cpp + any: true + - extension: test.cc + any: true + - excludes : + - Greater.test.cpp + - LeakyRelu.test.cpp + - Dequantize.test.cpp + - L2Normalize.test.cpp + - OneHot.test.cpp + - BatchToSpaceND.test.cpp + - BatchMatMul.test.cpp + - SpaceToBatchND.test.cpp + - LocalResponseNormalization.test.cpp + - LessEqual.test.cpp + - Minimum.test.cpp + - Relu6.test.cpp + - ResizeBilinear.test.cpp + - SquaredDifference.test.cpp + - SpaceToDepth.test.cpp + - SVDF.test.cpp + - Neg.test.cpp + - InstanceNorm.test.cpp + - MirrorPad.test.cpp + - Quantize.test.cpp + - ResizeNearestNeighbor.test.cpp + - LogicalNot.test.cpp + - Elu.test.cpp + - If.test.cpp + - ReverseV2.test.cpp + - Equal.test.cpp + - FloorDiv.test.cpp + - Rsqrt.test.cpp + - L2Pool2D.test.cpp + - PRelu.test.cpp + - TransposeConv.test.cpp + - ArgMax.test.cpp + - LogicalOr.test.cpp + - Div.test.cpp + - LogicalAnd.test.cpp + - Square.test.cpp + - AveragePool2D.test.cpp + - Pow.test.cpp + - Softmax.test.cpp + - NotEqual.test.cpp + - Cast.test.cpp + - Floor.test.cpp + - Exp.test.cpp + - GreaterEqual.test.cpp + - Maximum.test.cpp + - Mean.test.cpp + - PadV2.test.cpp + - Squeeze.test.cpp + - Pad.test.cpp + - DepthwiseConv2D.test.cpp + - Sqrt.test.cpp + - Relu.test.cpp + - LogSoftmax.test.cpp + - DepthToSpace.test.cpp + - Unpack.test.cpp + testCase: + - condition: + - functionName: + starts: + - TEST + - TYPED_TEST + - excludes : + - Verifier.dag_checker + negativeTestCase: + - condition: + - testName: + ends: + - _NEG + + positiveTestCase: + - condition: + - testName: + ends: + - _P diff --git a/.github/workflows/run-onert-micro-unit-tests.yml b/.github/workflows/run-onert-micro-unit-tests.yml new file mode 100644 index 0000000..8b27e63 --- /dev/null +++ b/.github/workflows/run-onert-micro-unit-tests.yml @@ -0,0 +1,47 @@ +name: Run onert-micro Unit tests + +on: + pull_request: + branches: + - master + - release/* + types: + - opened + - synchronize + - reopened + - ready_for_review + paths: + - 'onert-micro/**' + - '.github/workflows/run-onert-micro-unit-tests.yml' + +defaults: + run: + shell: bash + +jobs: + run-onert-micro-unit-tests: + name: Run onert-micro Unit tests + runs-on: ubuntu-20.04 + # Skip on draft, check on draft -> ready + if: github.event.pull_request.draft == false + + steps: + - name: Install Arm GNU Toolchain (arm-none-eabi-gcc) + uses: carlosperate/arm-none-eabi-gcc-action@v1 + with: + release: '12.2.Rel1' # <-- The compiler release to use + - name: Checkout + uses: actions/checkout@v3 + with: + # Checkout PR head commit + # Checkout Action use merge commit as default + ref: ${{ github.event.pull_request.head.sha }} + # Fetch all history and branch (default: 1) + fetch-depth: 0 + - name: Build and Run Tests + run: | + mkdir build + cd build + cmake ../infra/onert-micro/ -DENABLE_ONERT_MICRO_TEST=1 -DENABLE_TEST=1 + make -j$(nproc) luci_interpreter_kernels_micro_test + ./onert-micro/eval-driver/luci-interpreter/src/kernels/luci_interpreter_kernels_micro_test diff --git a/Makefile.template b/Makefile.template index 2e5d0bd..7621a2f 100644 --- a/Makefile.template +++ b/Makefile.template @@ -96,6 +96,12 @@ TIMESTAMP_INSTALL=$(WORKSPACE)/INSTALL export NNFW_WORKSPACE=$(WORKSPACE) ### +### Common environment variable for compiler module +### +NNCC_FOLDER=Product/$(WORKFOLDER)/nncc +export NNCC_WORKSPACE=$(NNCC_FOLDER) + +### ### Default target ### all: install @@ -103,6 +109,8 @@ all: install ### ### Command (public) ### +prepare-nncc: prepare_nncc_internal + configure: configure_internal build: build_internal @@ -135,6 +143,21 @@ create_acl_tar: acl_tar_internal $(WORKSPACE): mkdir -p $@ +prepare_nncc_internal: $(WORKSPACE) +ifneq ($(CROSS_BUILD),1) + ./nncc configure -DBUILD_GTEST=OFF -DENABLE_TEST=OFF -DEXTERNALS_BUILD_THREADS=$(NPROCS) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \ + -DCMAKE_INSTALL_PREFIX=$(OVERLAY_FOLDER) \ + -DBUILD_WHITELIST="luci;foder;pepper-csv2vec;loco;locop;logo;logo-core;mio-circle06;luci-compute;oops;hermes;hermes-std;angkor;pp;pepper-strcast;pepper-str" + ./nncc build -j$(NPROCS) + cmake --install $(NNCC_FOLDER) +# install angkor TensorIndex and oops InternalExn header (TODO: Remove this) + @mkdir -p ${OVERLAY_FOLDER}/include/nncc/core/ADT/tensor + @mkdir -p ${OVERLAY_FOLDER}/include/oops + @cp compiler/angkor/include/nncc/core/ADT/tensor/Index.h ${OVERLAY_FOLDER}/include/nncc/core/ADT/tensor + @cp compiler/oops/include/oops/InternalExn.h ${OVERLAY_FOLDER}/include/oops +endif + @echo "Done prepare-nncc" + configure_internal: $(WORKSPACE) ifneq ($(DEBIAN_BUILD),) test -d externals || mkdir -p externals @@ -165,9 +188,14 @@ acl_tar_internal: configure_internal install_acl_internal: # Workaround to install acl for test (ignore error when there is no file to copy) - cp $(OVERLAY_FOLDER)/lib/libarm_compute*.so $(INSTALL_ALIAS)/lib || true + @cp $(OVERLAY_FOLDER)/lib/libarm_compute*.so $(INSTALL_ALIAS)/lib 2>/dev/null || true + +install_luci_internal: + @mkdir -p $(INSTALL_ALIAS)/lib/nnfw/odc + @cp $(OVERLAY_FOLDER)/lib/libluci*.so $(INSTALL_ALIAS)/lib/nnfw/odc 2>/dev/null || true + @cp $(OVERLAY_FOLDER)/lib/libloco*.so $(INSTALL_ALIAS)/lib/nnfw/odc 2>/dev/null || true -install_all_internal: install_internal install_acl_internal +install_all_internal: install_internal install_acl_internal install_luci_internal test_suite_internal: install_all_internal @echo "packaging test suite" diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 2588cf0..ef13df8 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -12,8 +12,8 @@ else() message(STATUS "WARNING: lsb_release not found") endif() -if(${ONE_UBUNTU_CODENAME} STREQUAL "jammy") - set(ONE_UBUNTU_CODENAME_JAMMY TRUE) +if(${ONE_UBUNTU_CODENAME} STREQUAL "bionic") + set(ONE_UBUNTU_CODENAME_BIONIC TRUE) endif() # TODO Validate the argument of "requires" diff --git a/compiler/arser/CMakeLists.txt b/compiler/arser/CMakeLists.txt index 7eda215..b937ff6 100644 --- a/compiler/arser/CMakeLists.txt +++ b/compiler/arser/CMakeLists.txt @@ -2,7 +2,7 @@ add_library(arser INTERFACE) # It specifies INTERFACE so that future targets linked with arser library will inherit its include directory. # It means that a developer who want to link arser just need to add one line. -# target_link_library(another-users-target arser) +# target_link_library(another-users-target arser) target_include_directories(arser INTERFACE include/) target_link_libraries(arser INTERFACE nncc_coverage) diff --git a/compiler/circle-eval-diff/src/InputDataLoader.cpp b/compiler/circle-eval-diff/src/InputDataLoader.cpp index f15b2ba..7b491a3 100644 --- a/compiler/circle-eval-diff/src/InputDataLoader.cpp +++ b/compiler/circle-eval-diff/src/InputDataLoader.cpp @@ -126,17 +126,19 @@ InputDataLoader::Data HDF5Loader::get(uint32_t data_idx) const data.at(input_idx) = *createEmptyTensor(input_node).get(); auto input_buffer = data.at(input_idx).buffer(); + const auto input_buffer_bytes = data.at(input_idx).byte_size(); + try { if (_hdf5->isRawData()) { - _hdf5->readTensor(data_idx, input_idx, input_buffer); + _hdf5->readTensor(data_idx, input_idx, input_buffer, input_buffer_bytes); } else { DataType dtype; Shape shape; - _hdf5->readTensor(data_idx, input_idx, &dtype, &shape, input_buffer); + _hdf5->readTensor(data_idx, input_idx, &dtype, &shape, input_buffer, input_buffer_bytes); // Check the type and the shape of the input data is valid verifyTypeShape(input_node, dtype, shape); @@ -164,7 +166,7 @@ DirectoryLoader::DirectoryLoader(const std::string &dir_path, struct dirent *entry = nullptr; const auto input_total_bytes = getTotalByteSizeOf(input_nodes); - while (entry = readdir(dir)) + while ((entry = readdir(dir))) { // Skip if the entry is not a regular file if (entry->d_type != DT_REG) diff --git a/compiler/circle-inspect/CMakeLists.txt b/compiler/circle-inspect/CMakeLists.txt index 10d26d1..8edfde4 100644 --- a/compiler/circle-inspect/CMakeLists.txt +++ b/compiler/circle-inspect/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_circle04) +if(NOT TARGET mio_circle06) return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) set(DRIVER "driver/Driver.cpp") @@ -10,6 +10,6 @@ add_executable(circle-inspect ${DRIVER} ${SOURCES}) target_include_directories(circle-inspect PRIVATE src) target_link_libraries(circle-inspect arser) target_link_libraries(circle-inspect foder) -target_link_libraries(circle-inspect mio_circle04) -target_link_libraries(circle-inspect mio_circle04_helper) +target_link_libraries(circle-inspect mio_circle06) +target_link_libraries(circle-inspect mio_circle06_helper) target_link_libraries(circle-inspect safemain) diff --git a/compiler/circle-inspect/requires.cmake b/compiler/circle-inspect/requires.cmake index 183dfe2..b3a2638 100644 --- a/compiler/circle-inspect/requires.cmake +++ b/compiler/circle-inspect/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-circle04") +require("mio-circle06") require("safemain") diff --git a/compiler/circle-interpreter-test/CMakeLists.txt b/compiler/circle-interpreter-test/CMakeLists.txt index 50cd0c6..fbb0fcd 100644 --- a/compiler/circle-interpreter-test/CMakeLists.txt +++ b/compiler/circle-interpreter-test/CMakeLists.txt @@ -11,7 +11,17 @@ nnas_find_package(GTest REQUIRED) file(GLOB_RECURSE TESTS "src/*.test.cpp") GTest_AddTest(circle-interpreter-test ${TESTS}) -set_tests_properties(circle-interpreter-test - PROPERTIES - ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH};CIRCLE_INTERPRETER_PATH=${CIRCLE_INTERPRETER_PATH}" - ) +# circle-interpreter-test uses input data generated during luci_value_test +if(NOT CMAKE_CROSSCOMPILING) + set_tests_properties(circle-interpreter-test + PROPERTIES + DEPENDS luci_value_test + ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH};CIRCLE_INTERPRETER_PATH=${CIRCLE_INTERPRETER_PATH}" + ) +else(NOT CMAKE_CROSSCOMPILING) + set_tests_properties(circle-interpreter-test + PROPERTIES + DEPENDS luci_value_cross_test + ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH};CIRCLE_INTERPRETER_PATH=${CIRCLE_INTERPRETER_PATH}" + ) +endif(NOT CMAKE_CROSSCOMPILING) diff --git a/compiler/circle-interpreter-test/requires.cmake b/compiler/circle-interpreter-test/requires.cmake index 8d8585b..5ca5749 100644 --- a/compiler/circle-interpreter-test/requires.cmake +++ b/compiler/circle-interpreter-test/requires.cmake @@ -1,2 +1,3 @@ require("common-artifacts") require("circle-interpreter") +require("luci-value-test") diff --git a/compiler/circle-mpqsolver/CMakeLists.txt b/compiler/circle-mpqsolver/CMakeLists.txt index 25e318e..9af9fc2 100644 --- a/compiler/circle-mpqsolver/CMakeLists.txt +++ b/compiler/circle-mpqsolver/CMakeLists.txt @@ -1,13 +1,20 @@ +nnas_find_package(Jsoncpp) +if(NOT Jsoncpp_FOUND) + message(STATUS "Build circle-mpqsolver: FAILED (missing jsoncpp)") + return() +endif(NOT Jsoncpp_FOUND) + file(GLOB_RECURSE SOURCES "src/*.cpp") file(GLOB_RECURSE TESTS "src/*.test.cpp") list(REMOVE_ITEM SOURCES ${TESTS}) add_executable(circle-mpqsolver "${SOURCES}") target_include_directories(circle-mpqsolver PRIVATE src) +target_include_directories(circle-mpqsolver PRIVATE ${Jsoncpp_INCLUDE_DIRS}) +target_link_libraries(circle-mpqsolver ${Jsoncpp_STATIC_LIB}) target_link_libraries(circle-mpqsolver arser) target_link_libraries(circle-mpqsolver vconone) target_link_libraries(circle-mpqsolver safemain) -target_link_libraries(circle-mpqsolver luci_lang) target_link_libraries(circle-mpqsolver luci_service) target_link_libraries(circle-mpqsolver luci_pass) target_link_libraries(circle-mpqsolver luci_interpreter) @@ -27,11 +34,15 @@ endif(NOT ENABLE_TEST) # Instead, we use TEST_SOURCES to specify sources uesd for tests. set(TEST_SOURCES "src/bisection/DepthParameterizer.cpp" - "src/bisection/Quantizer.cpp" - "src/bisection/ErrorApproximator.cpp") + "src/core/Quantizer.cpp" + "src/bisection/VISQErrorApproximator.cpp" + "src/core/ErrorMetric.cpp" +) nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_mpqsolver_test ${TESTS} ${TEST_SOURCES}) -target_link_libraries(circle_mpqsolver_test luci_lang) +target_include_directories(circle_mpqsolver_test PRIVATE src) +target_include_directories(circle_mpqsolver_test PRIVATE ${Jsoncpp_INCLUDE_DIRS}) +target_link_libraries(circle_mpqsolver_test ${Jsoncpp_STATIC_LIB}) target_link_libraries(circle_mpqsolver_test luci_service) target_link_libraries(circle_mpqsolver_test luci_pass) diff --git a/compiler/circle-mpqsolver/README.md b/compiler/circle-mpqsolver/README.md index 9f4c2e7..aa66e28 100644 --- a/compiler/circle-mpqsolver/README.md +++ b/compiler/circle-mpqsolver/README.md @@ -39,6 +39,8 @@ Run _circle-mpqsolver_ with the following arguments. --qerror_ratio: Target quantization error ratio. It should be in [0, 1]. 0 indicates qerror of full int16 model, 1 indicates qerror of full uint8 model. The lower `qerror_ratio` indicates the more accurate solution. --bisection _mode_: input nodes should be at Q16 precision ['auto', 'true', 'false'] +--visq_file: .visq.json file to be used in 'auto' mode +--save_intermediate: path to the directory where all intermediate results will be saved ``` $ ./circle-mpqsolver @@ -47,6 +49,8 @@ $ ./circle-mpqsolver --output_model --qerror_ratio --bisection + --visq_file <*.visq.json file with quantization errors> + --save_intermediate ``` For example: diff --git a/compiler/circle-mpqsolver/src/CircleMPQSolver.cpp b/compiler/circle-mpqsolver/src/CircleMPQSolver.cpp index 23e8fd4..12981be 100644 --- a/compiler/circle-mpqsolver/src/CircleMPQSolver.cpp +++ b/compiler/circle-mpqsolver/src/CircleMPQSolver.cpp @@ -18,13 +18,12 @@ #include #include #include -#include #include "bisection/BisectionSolver.h" +#include #include #include -#include void print_version(void) { @@ -32,11 +31,23 @@ void print_version(void) std::cout << vconone::get_copyright() << std::endl; } -int entry(int argc, char **argv) +int handleAutoAlgorithm(arser::Arser &arser, mpqsolver::bisection::BisectionSolver &solver) { - LOGGER(l); + solver.algorithm(mpqsolver::bisection::BisectionSolver::Algorithm::Auto); + auto data_path = arser.get("--visq_file"); + if (data_path.empty()) + { + std::cerr << "ERROR: please provide visq_file for auto mode" << std::endl; + return false; + } + solver.setVisqPath(data_path); + return true; +} +int entry(int argc, char **argv) +{ const std::string bisection_str = "--bisection"; + const std::string save_intermediate_str = "--save_intermediate"; arser::Arser arser("circle-mpqsolver provides light-weight methods for finding a high-quality " "mixed-precision model within a reasonable time."); @@ -74,6 +85,17 @@ int entry(int argc, char **argv) arser.add_argument("--output_model").required(true).help("Output quantized model"); + arser.add_argument("--visq_file") + .type(arser::DataType::STR) + .default_value("") + .required(false) + .help("*.visq.json file with quantization errors"); + + arser.add_argument(save_intermediate_str) + .type(arser::DataType::STR) + .required(false) + .help("path to save intermediate results"); + try { arser.parse(argc, argv); @@ -104,7 +126,12 @@ int entry(int argc, char **argv) std::cerr << "ERROR: quantization ratio must be in [0, 1]" << std::endl; return EXIT_FAILURE; } - auto start = std::chrono::high_resolution_clock::now(); + + SolverOutput::get() << ">> Searching mixed precision configuration \n" + << "model:" << input_model_path << "\n" + << "dataset: " << data_path << "\n" + << "input dtype: " << input_dtype << "\n" + << "output dtype: " << output_dtype << "\n"; if (arser[bisection_str]) { @@ -116,14 +143,20 @@ int entry(int argc, char **argv) auto value = arser.get(bisection_str); if (value == "auto") { - solver.algorithm(BisectionSolver::Algorithm::Auto); + SolverOutput::get() << "algorithm: bisection (auto)\n"; + if (!handleAutoAlgorithm(arser, solver)) + { + return EXIT_FAILURE; + } } else if (value == "true") { + SolverOutput::get() << "algorithm: bisection (Q16AtFront)"; solver.algorithm(BisectionSolver::Algorithm::ForceQ16Front); } else if (value == "false") { + SolverOutput::get() << "algorithm: bisection (Q8AtFront)"; solver.algorithm(BisectionSolver::Algorithm::ForceQ16Back); } else @@ -134,6 +167,18 @@ int entry(int argc, char **argv) } } + if (arser[save_intermediate_str]) + { + auto data_path = arser.get(save_intermediate_str); + if (!data_path.empty()) + { + solver.set_save_intermediate(data_path); + } + } + + SolverOutput::get() << "qerror metric: MAE\n" + << "target qerror ratio: " << qerror_ratio << "\n"; + auto optimized = solver.run(input_model_path); if (optimized == nullptr) { @@ -143,6 +188,7 @@ int entry(int argc, char **argv) // save optimized { + SolverOutput::get() << "Saving output model to " << output_model_path << "\n"; luci::CircleExporter exporter; luci::CircleFileExpContract contract(optimized.get(), output_model_path); if (!exporter.invoke(&contract)) @@ -159,10 +205,5 @@ int entry(int argc, char **argv) return EXIT_FAILURE; } - auto duration = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start); - VERBOSE(l, 0) << "Elapsed Time: " << std::setprecision(5) << duration.count() / 60.f - << " minutes." << std::endl; - return EXIT_SUCCESS; } diff --git a/compiler/circle-mpqsolver/src/MPQSolver.cpp b/compiler/circle-mpqsolver/src/MPQSolver.cpp index 02732eb..10cfbb6 100644 --- a/compiler/circle-mpqsolver/src/MPQSolver.cpp +++ b/compiler/circle-mpqsolver/src/MPQSolver.cpp @@ -24,3 +24,8 @@ MPQSolver::MPQSolver(const std::string &input_data_path, float qerror_ratio, _input_quantization(input_quantization), _output_quantization(output_quantization) { } + +void MPQSolver::set_save_intermediate(const std::string &save_path) +{ + _hooks = std::make_unique(save_path); +} diff --git a/compiler/circle-mpqsolver/src/MPQSolver.h b/compiler/circle-mpqsolver/src/MPQSolver.h index 7c018d0..6c5d25d 100644 --- a/compiler/circle-mpqsolver/src/MPQSolver.h +++ b/compiler/circle-mpqsolver/src/MPQSolver.h @@ -17,7 +17,7 @@ #ifndef __MPQSOLVER_MPQSOLEVR_SOLVER_H__ #define __MPQSOLVER_MPQSOLEVR_SOLVER_H__ -#include +#include #include #include @@ -42,11 +42,17 @@ public: */ virtual std::unique_ptr run(const std::string &module_path) = 0; + /** + * @brief set all intermediate artifacts to be saved + */ + void set_save_intermediate(const std::string &save_path); + protected: std::string _input_data_path; std::string _input_quantization; std::string _output_quantization; float _qerror_ratio = 0.f; // quantization error ratio + std::unique_ptr _hooks; }; } // namespace mpqsolver diff --git a/compiler/circle-mpqsolver/src/bisection/BisectionSolver.cpp b/compiler/circle-mpqsolver/src/bisection/BisectionSolver.cpp index d506802..976dac5 100644 --- a/compiler/circle-mpqsolver/src/bisection/BisectionSolver.cpp +++ b/compiler/circle-mpqsolver/src/bisection/BisectionSolver.cpp @@ -16,11 +16,12 @@ #include "BisectionSolver.h" #include "DepthParameterizer.h" -#include "ErrorMetric.h" -#include "ErrorApproximator.h" +#include "VISQErrorApproximator.h" + +#include +#include #include -#include #include #include @@ -30,15 +31,23 @@ using namespace mpqsolver::bisection; namespace { -bool error_at_input_is_larger_than_at_output(const NodeDepthType &nodes_depth, float cut_depth) +/** + * @brief Compare errors of two disjoint subsets of a model sliced by cut_depth + * @return True if the front part (< cut_depth) has larger errors than the rear part (>= cut_depth) + */ +bool front_has_higher_error(const NodeDepthType &nodes_depth, const std::string &visq_path, + float cut_depth) { - LOGGER(l); + SolverOutput::get() << "\n>> Running bisection(auto) algorithm\n"; + + VISQErrorApproximator approximator; + approximator.init(visq_path); float error_at_input = 0; float error_at_output = 0; for (auto &iter : nodes_depth) { - float cur_error = approximate(iter.first); + float cur_error = approximator.approximate(iter.first->name()); if (iter.second < cut_depth) { error_at_input += cur_error; @@ -49,16 +58,16 @@ bool error_at_input_is_larger_than_at_output(const NodeDepthType &nodes_depth, f } } + SolverOutput::get() << "Qerror of front half: " << error_at_input << "\n"; + SolverOutput::get() << "Qerror of rear half: " << error_at_output << "\n"; if (error_at_input > error_at_output) { - VERBOSE(l, 0) << "Q16 will be set at input due to "; + SolverOutput::get() << "Front part will be Q16, while the rear will be Q8\n"; } else { - VERBOSE(l, 0) << "Q8 will be set at input due to "; + SolverOutput::get() << "Front part will be Q8, while the rear will be Q16\n"; } - VERBOSE(l, 0) << error_at_input << " error at input vs "; - VERBOSE(l, 0) << error_at_output << " error at output." << std::endl; return error_at_input > error_at_output; } @@ -83,11 +92,12 @@ BisectionSolver::BisectionSolver(const std::string &input_data_path, float qerro const std::string &output_quantization) : MPQSolver(input_data_path, qerror_ratio, input_quantization, output_quantization) { - _quantizer = std::make_unique(_input_quantization, _output_quantization); + _quantizer = std::make_unique(_input_quantization, _output_quantization); } -float BisectionSolver::evaluate(const DatasetEvaluator &evaluator, const std::string &flt_path, - const std::string &def_quant, LayerParams &layers) +float BisectionSolver::evaluate(const core::DatasetEvaluator &evaluator, + const std::string &flt_path, const std::string &def_quant, + core::LayerParams &layers) { auto model = read_module(flt_path); // get fake quantized model for evaluation @@ -101,10 +111,10 @@ float BisectionSolver::evaluate(const DatasetEvaluator &evaluator, const std::st void BisectionSolver::algorithm(Algorithm algorithm) { _algorithm = algorithm; } +void BisectionSolver::setVisqPath(const std::string &visq_path) { _visq_data_path = visq_path; } + std::unique_ptr BisectionSolver::run(const std::string &module_path) { - LOGGER(l); - auto module = read_module(module_path); float min_depth = 0.f; @@ -117,17 +127,24 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat return nullptr; } - std::unique_ptr metric = std::make_unique(); - DatasetEvaluator evaluator(module.get(), _input_data_path, *metric.get()); + SolverOutput::get() << "\n>> Computing baseline qerrors\n"; + + std::unique_ptr metric = std::make_unique(); + core::DatasetEvaluator evaluator(module.get(), _input_data_path, *metric.get()); - LayerParams layer_params; + core::LayerParams layer_params; float int16_qerror = evaluate(evaluator, module_path, "int16" /* default quant_dtype */, layer_params); - VERBOSE(l, 0) << "Full int16 model quantization error " << int16_qerror << std::endl; + SolverOutput::get() << "Full int16 model qerror: " << int16_qerror << "\n"; float uint8_qerror = evaluate(evaluator, module_path, "uint8" /* default quant_dtype */, layer_params); - VERBOSE(l, 0) << "Full uint8 model quantization error " << uint8_qerror << std::endl; + SolverOutput::get() << "Full uint8 model qerror: " << uint8_qerror << "\n"; + _quantizer->set_hook(_hooks.get()); + if (_hooks) + { + _hooks->on_begin_solver(module_path, uint8_qerror, int16_qerror); + } if (int16_qerror > uint8_qerror) { @@ -135,7 +152,7 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat } _qerror = int16_qerror + _qerror_ratio * std::fabs(uint8_qerror - int16_qerror); - VERBOSE(l, 0) << "Target quantization error " << _qerror << std::endl; + SolverOutput::get() << "Target qerror: " << _qerror << "\n"; if (uint8_qerror <= _qerror) { @@ -149,7 +166,8 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat int last_depth = -1; float best_depth = -1; - LayerParams best_params; + float best_accuracy = -1; + core::LayerParams best_params; if (module->size() != 1) { throw std::runtime_error("Unsupported module"); @@ -173,27 +191,40 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat { case Algorithm::Auto: int16_front = - error_at_input_is_larger_than_at_output(nodes_depth, 0.5f * (max_depth + min_depth)); + front_has_higher_error(nodes_depth, _visq_data_path, 0.5f * (max_depth + min_depth)); break; case Algorithm::ForceQ16Front: + SolverOutput::get() << "Front part will be Q16, while the rear will be Q8\n"; int16_front = true; break; case Algorithm::ForceQ16Back: - int16_front = true; + SolverOutput::get() << "Front part will be Q8, while the rear will be Q16\n"; + int16_front = false; break; } + SolverOutput::get() << "\n"; + while (true) { + if (_hooks) + { + _hooks->on_begin_iteration(); + } + int cut_depth = static_cast(std::floor(0.5f * (min_depth + max_depth))); if (last_depth == cut_depth) { break; } + + SolverOutput::get() << "Looking for the optimal configuration in [" << min_depth << " , " + << max_depth << "] depth segment\n"; + last_depth = cut_depth; - LayerParams layer_params; + core::LayerParams layer_params; for (auto &node : active_nodes) { auto cur_node = loco::must_cast(node); @@ -207,7 +238,7 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat if ((depth <= cut_depth && int16_front) || (depth >= cut_depth && !int16_front)) { - auto layer_param = std::make_shared(); + auto layer_param = std::make_shared(); { layer_param->name = cur_node->name(); layer_param->dtype = "int16"; @@ -219,21 +250,36 @@ std::unique_ptr BisectionSolver::run(const std::string &module_pat } float cur_accuracy = evaluate(evaluator, module_path, "uint8", layer_params); - VERBOSE(l, 0) << cut_depth << " : " << cur_accuracy << std::endl; + + if (_hooks) + { + _hooks->on_end_iteration(layer_params, "uint8", cur_accuracy); + } if (cur_accuracy < _qerror) { + SolverOutput::get() << "Qerror at depth " << cut_depth << " is " << cur_accuracy + << " < target qerror (" << _qerror << ")\n"; int16_front ? (max_depth = cut_depth) : (min_depth = cut_depth); best_params = layer_params; best_depth = cut_depth; + best_accuracy = cur_accuracy; } else { + SolverOutput::get() << "Qerror at depth " << cut_depth << " is " << cur_accuracy + << (cur_accuracy > _qerror ? " > " : " == ") << "target qerror (" + << _qerror << ")\n"; int16_front ? (min_depth = cut_depth) : (max_depth = cut_depth); } } - VERBOSE(l, 0) << "Found the best configuration at " << best_depth << " depth." << std::endl; + if (_hooks) + { + _hooks->on_end_solver(best_params, "uint8", best_accuracy); + } + + SolverOutput::get() << "Found the best configuration at depth " << best_depth << "\n"; if (!_quantizer->quantize(module.get(), "uint8", best_params)) { std::cerr << "ERROR: Failed to quantize model" << std::endl; diff --git a/compiler/circle-mpqsolver/src/bisection/BisectionSolver.h b/compiler/circle-mpqsolver/src/bisection/BisectionSolver.h index 7ec2e69..83851c0 100644 --- a/compiler/circle-mpqsolver/src/bisection/BisectionSolver.h +++ b/compiler/circle-mpqsolver/src/bisection/BisectionSolver.h @@ -17,8 +17,8 @@ #ifndef __MPQSOLVER_BISECTION_SOLVER_H__ #define __MPQSOLVER_BISECTION_SOLVER_H__ -#include "Quantizer.h" -#include "Evaluator.h" +#include +#include #include #include @@ -64,14 +64,22 @@ public: */ void algorithm(Algorithm algorithm); + /** + * @brief set visq_file path to be used in 'auto' mode + * @details this is used to handle which way (8 or 16bit) of + * splitting the neural network will be the best for accuracy. + */ + void setVisqPath(const std::string &visq_path); + private: - float evaluate(const DatasetEvaluator &evaluator, const std::string &module_path, - const std::string &def_quant, LayerParams &layers); + float evaluate(const core::DatasetEvaluator &evaluator, const std::string &module_path, + const std::string &def_quant, core::LayerParams &layers); private: float _qerror = 0.f; // quantization error Algorithm _algorithm = Algorithm::ForceQ16Front; - std::unique_ptr _quantizer; + std::unique_ptr _quantizer; + std::string _visq_data_path; }; } // namespace bisection diff --git a/compiler/circle-mpqsolver/src/bisection/DepthParameterizer.test.cpp b/compiler/circle-mpqsolver/src/bisection/DepthParameterizer.test.cpp index d3865e4..504032d 100644 --- a/compiler/circle-mpqsolver/src/bisection/DepthParameterizer.test.cpp +++ b/compiler/circle-mpqsolver/src/bisection/DepthParameterizer.test.cpp @@ -17,7 +17,7 @@ #include #include "DepthParameterizer.h" -#include "TestHelper.h" +#include #include diff --git a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.cpp b/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.cpp deleted file mode 100644 index ebd7258..0000000 --- a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ErrorApproximator.h" - -#include -#include -#include -#include -#include - -namespace -{ - -using namespace luci; -using IterFunc = std::function; - -inline bool has_min_max(const CircleNode *node) -{ - return node->quantparam() && !node->quantparam()->min.empty() && !node->quantparam()->max.empty(); -} - -inline uint32_t cal_offset(const loco::TensorShape &dimension, uint32_t *indices) -{ - return indices[0] * dimension.dim(1).value() * dimension.dim(2).value() * - dimension.dim(3).value() + - indices[1] * dimension.dim(2).value() * dimension.dim(3).value() + - indices[2] * dimension.dim(3).value() + indices[3]; -} - -uint32_t get_channel_dim_index(const CircleNode *node) -{ - uint32_t index = 0; - auto opcode = node->opcode(); - switch (opcode) - { - case CircleOpcode::CONV_2D: - case CircleOpcode::TRANSPOSE_CONV: - case CircleOpcode::FULLY_CONNECTED: - index = 0; - break; - case CircleOpcode::DEPTHWISE_CONV_2D: - index = 3; - break; - default: - throw std::runtime_error("Failed to find channel index in " + node->name()); - } - - return index; -} - -bool set_weight_dim(const CircleNode *node, const CircleConst *weights, - loco::TensorShape &dimension) -{ - auto opcode = node->opcode(); - switch (opcode) - { - case CircleOpcode::CONV_2D: - case CircleOpcode::TRANSPOSE_CONV: - case CircleOpcode::DEPTHWISE_CONV_2D: - assert(node->rank() == 4); - dimension.rank(node->rank()); - dimension.dim(0).set(weights->dim(0).value()); - dimension.dim(1).set(weights->dim(1).value()); - dimension.dim(2).set(weights->dim(2).value()); - dimension.dim(3).set(weights->dim(3).value()); - break; - case CircleOpcode::FULLY_CONNECTED: - assert(node->rank() == 2); - dimension.rank(4); - dimension.dim(0).set(weights->dim(0).value()); - dimension.dim(1).set(1); // Set FC layer like CONV - dimension.dim(2).set(1); - dimension.dim(3).set(weights->dim(1).value()); - break; - default: - return false; - } - - return true; -} - -loco::Node *get_weight(const CircleNode *node) -{ - loco::Node *weight = nullptr; - auto opcode = node->opcode(); - switch (opcode) - { - case CircleOpcode::CONV_2D: - { - auto conv = loco::must_cast(node); - weight = conv->filter(); - } - break; - case CircleOpcode::DEPTHWISE_CONV_2D: - { - auto dconv = loco::must_cast(node); - weight = dconv->filter(); - } - break; - case CircleOpcode::TRANSPOSE_CONV: - { - auto tconv = loco::must_cast(node); - weight = tconv->filter(); - } - break; - case CircleOpcode::FULLY_CONNECTED: - { - auto fc = loco::must_cast(node); - weight = fc->weights(); - } - break; - default: - break; - } - - return weight; -} - -inline CircleConst *get_constant_weight(const CircleNode *node) -{ - CircleConst *weight = dynamic_cast(get_weight(node)); - if (weight == nullptr) - { - throw std::runtime_error("Unsupported non-constant weights in convolution node " + - node->name()); - } - - return weight; -} - -void iterate_per_channel(const CircleNode *node, IterFunc func) -{ - CircleConst *weight = get_constant_weight(node); - - loco::TensorShape dimension; - set_weight_dim(node, weight, dimension); - uint32_t indices[4] = { - 0, - }; - - auto channel_dim_index = get_channel_dim_index(node); - - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - func(indices, dimension, channel_dim_index); - } - } - } - } -} - -void cal_minmax_per_channel(const CircleNode *node, std::vector &min, - std::vector &max) -{ - CircleConst *weight = get_constant_weight(node); - - loco::TensorShape dimension; - set_weight_dim(node, weight, dimension); - - auto channel_dim_index = get_channel_dim_index(node); - auto size = dimension.dim(channel_dim_index).value(); - - std::vector has_min_max_value(size, false); - min.resize(size); - max.resize(size); - - auto cal_minmax = [&](uint32_t *indices, loco::TensorShape &dimension, - uint32_t channel_dim_index) { - uint32_t channel_idx = indices[channel_dim_index]; - auto data = weight->at(cal_offset(dimension, indices)); - if (has_min_max_value[channel_idx]) - { - min[channel_idx] = data < min[channel_idx] ? data : min[channel_idx]; - max[channel_idx] = data > max[channel_idx] ? data : max[channel_idx]; - } - else - { - min[channel_idx] = data; - max[channel_idx] = data; - has_min_max_value[channel_idx] = true; - } - }; - - iterate_per_channel(node, cal_minmax); -} - -bool get_shape(const CircleNode *circle_node, std::vector &shape) -{ - if (circle_node->shape_status() == ShapeStatus::VALID) - { - auto rank = circle_node->rank(); - if (rank != 4) - return false; - - shape.resize(rank); - for (uint32_t i = 0; i < rank; i++) - { - shape[i] = circle_node->dim(i).value(); - } - return true; - } - - return false; -} - -/** - * @brief get_additions_per_channel computes W * H * CIN * KW * KH. - * - * W, H - width/height of OFM; KW, KH - convolution kernel width/height; - * CIN - number of channels in IFM (for depthwise its unity) - * See - * https://github.com/Samsung/ONE/pull/10170#discussion_r1065371638 - * for derivation. - */ -uint32_t get_additions_per_channel(const CircleNode *node) -{ - uint32_t adds_per_channel = 1; - std::vector ofm_shape; - if (!get_shape(node, ofm_shape)) // [BATCH, W, H, channels_out] - { - throw std::runtime_error("Failed to find correct shape " + node->name()); - } - - adds_per_channel *= ofm_shape[1] * ofm_shape[2]; // adds_per_channel *= W * H - - auto weights = loco::must_cast(get_weight(node)); - { - std::vector w_shape; - if (get_shape(weights, w_shape)) // [channels_out, k_x, k_y, channels_in] - { - adds_per_channel *= (w_shape[1] * w_shape[2]); // adds_per_channel *= k_x * k_y - } - if (node->opcode() != CircleOpcode::DEPTHWISE_CONV_2D) - { - // for not depthwise convolutions we need to scale it by CIN - adds_per_channel *= w_shape[3]; // adds_per_channel *= c_in - } - } - - return adds_per_channel; -} - -void get_min_max_ifm_values(const CircleNode *node, float &ci_min, float &ci_max) -{ - auto preds = loco::preds(node); - for (const auto &pred : preds) - { - auto parent_node = loco::must_cast(pred); - if (has_min_max(parent_node)) - { - auto quantparam = parent_node->quantparam(); - if (quantparam->min.size() > 0) - { - ci_min = quantparam->min[0]; - ci_max = quantparam->max[0]; - } - } - } -} - -/** - * @brief Return upper bound of quantization error for CONV, DCONV, TCONV. - * - * See - * https://github.com/Samsung/ONE/pull/10170#discussion_r1065371638 for details. - */ -float approximate_conv(const CircleNode *node) -{ - float volume_W_A_err = 0.f; - { - // activation min-max values - float ci_min = 0.f; - float ci_max = 0.f; - get_min_max_ifm_values(node, ci_min, ci_max); - - // channel-wise min, max - std::vector min_values; - std::vector max_values; - cal_minmax_per_channel(node, min_values, max_values); - assert(not min_values.empty()); - assert(not max_values.empty()); - - // ranges = (max_values - min_values) - std::vector ranges; - std::transform(max_values.begin(), max_values.end(), min_values.begin(), - std::back_inserter(ranges), std::minus()); - - // maximal weight value across all channels - float w_max = 0; - { - assert(max_values.size() == min_values.size()); - for (size_t i = 0; i < max_values.size(); ++i) - { - w_max = std::max(w_max, std::abs(max_values[i])); - w_max = std::max(w_max, std::abs(min_values[i])); - } - } - - // total weight quantization error across all channels - // so maximal error of quantization is ~ (max_value - min_value) / 255 - // omitting 255 term we get that maximal error of quantization is just its range - float sum_err = 0.f; - for (auto cur_err : ranges) - { - sum_err += cur_err; - } - - uint32_t adds_per_channel = get_additions_per_channel(node); - uint32_t num_of_channels = ranges.size(); - - // maximal error introduced by weights quantization (for all channels) - volume_W_A_err = sum_err * std::max(::fabs(ci_max), ::fabs(ci_min)); - // plus total error introduced by activation quantization (for all channels) - volume_W_A_err += w_max * num_of_channels * ::fabs(ci_max - ci_min); - // scale by volume of adds per channel - volume_W_A_err *= adds_per_channel; - // scale to get more readable output values - volume_W_A_err /= 1.e+6f; - } - - return volume_W_A_err; -} - -} // namespace - -namespace mpqsolver -{ -namespace bisection -{ - -/** - * How Approximate works? - * - * Currently it works just for convolution layers, but may be generalized for other types as well. - * See discussion at https://github.com/Samsung/ONE/pull/10170#discussion_r1042246598 - * Convolution can be expressed as a matrix multiplication. - * While quantizing we introduce quantization error into convolution operand (activations) as well - * as into convolution weights. A_q * W_q = (A + q_err(A)) * (W + q_err(W)) = A * W + A * q_err(W) + - * W * q_err(A) + q_err(A) * q_err(W), assuming q_err(A) * q_err(W) are negligible as quadratic - * terms, we get A_q * W_q ~ A * W + A * q_err(W) + W * q_err(A) , q_err - quantization error, - * W - weight matrix, A - activations from previous layer (IFM), so quantization error of matrix - * multiplication can be approximated as A * q_err(W) + W * q_err(A). Estimating its upper bound - * we get A * q_err(W) + W * q_err(A) <= - * number_of_additions * (A_max * (W_max - W_min) / 255 + W_max * (A_max - A_min) / 255) - * The following code tries to get total error for quantizing convolution node into Q8. - * It's just an heuristic (Metric sensitivity depends highly on derivatives as well). - */ -float approximate(const CircleNode *node) -{ - auto opcode = node->opcode(); - float qerror = 0.f; - switch (opcode) - { - case CircleOpcode::DEPTHWISE_CONV_2D: - case CircleOpcode::CONV_2D: - case CircleOpcode::TRANSPOSE_CONV: - qerror = approximate_conv(node); - break; - default: // TODO (FULLY_CONNECTED e.g.) - qerror = 0.f; - } - - return qerror; -} - -} // namespace bisection -} // namespace mpqsolver diff --git a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.test.cpp b/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.test.cpp deleted file mode 100644 index 015f83b..0000000 --- a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.test.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "ErrorApproximator.h" -#include "TestHelper.h" - -#include - -#include - -namespace -{ - -inline uint32_t cal_offset(uint32_t shape[4], uint32_t *indices) -{ - return indices[0] * shape[1] * shape[2] * shape[3] + indices[1] * shape[2] * shape[3] + - indices[2] * shape[3] + indices[3]; -} - -class NConvGraph final : public SimpleGraph -{ -protected: - void initInput(loco::Node *input) override - { - auto ci_input = loco::must_cast(input); - ci_input->shape_status(luci::ShapeStatus::VALID); - auto qparam = std::make_unique(); - qparam->min.assign(_channel_size, _a_min); - qparam->max.assign(_channel_size, _a_max); - ci_input->quantparam(std::move(qparam)); - } - - loco::Node *insertGraphBody(loco::Node *input) override - { - _filter = _g->nodes()->create(); - _filter->dtype(loco::DataType::FLOAT32); - _filter->shape({_channel_size, _f_w, _f_h, _channel_size}); - _filter->shape_status(luci::ShapeStatus::VALID); - _filter->name("conv_filter"); - uint32_t indices[4] = { - 0, - }; - - uint32_t w_shape[4] = {_filter->dim(0).value(), _filter->dim(1).value(), - _filter->dim(2).value(), _filter->dim(3).value()}; - - _filter->size(w_shape[0] * w_shape[1] * w_shape[2] * w_shape[3]); - - for (indices[0] = 0; indices[0] < w_shape[0]; ++indices[0]) - { - for (indices[1] = 0; indices[1] < w_shape[1]; ++indices[1]) - { - for (indices[2] = 0; indices[2] < w_shape[2]; ++indices[2]) - { - for (indices[3] = 0; indices[3] < w_shape[3]; ++indices[3]) - { - uint32_t offset = cal_offset(w_shape, indices); - _filter->at(offset) = (offset % 2 == 0) ? _w_max : _w_min; - } - } - } - } - - _bias = _g->nodes()->create(); - _bias->dtype(loco::DataType::FLOAT32); - _bias->shape({_channel_size}); - _bias->name("conv_bias"); - - _conv = _g->nodes()->create(); - _conv->padding(luci::Padding::SAME); - _conv->fusedActivationFunction(luci::FusedActFunc::NONE); - _conv->dtype(loco::DataType::FLOAT32); - _conv->shape({1, _width, _height, _channel_size}); - _conv->shape_status(luci::ShapeStatus::VALID); - _conv->name("conv"); - _conv->filter(_filter); - _conv->bias(_bias); - _conv->input(input); - - return _conv; - } - -public: - luci::CircleConv2D *_conv = nullptr; - luci::CircleConst *_filter = nullptr; - luci::CircleConst *_bias = nullptr; - uint32_t _f_w = 1; - uint32_t _f_h = 1; - float _w_min = -1.f; - float _w_max = 1.f; - float _a_min = -1.f; - float _a_max = 1.f; -}; - -} // namespace - -TEST(CircleMPQSolverErrorApproximatorTest, verifyResultsTest) -{ - NConvGraph g; - g.init(); - - auto value = mpqsolver::bisection::approximate(g._conv); - float expected = ((g._w_max - g._w_min) * g._channel_size * std::max(g._a_max, g._a_min) + - (g._a_max - g._a_min) * g._channel_size * std::max(g._w_max, g._w_min)) * - g._f_h * g._f_w * g._height * g._width * g._channel_size / 1.e+6f; - EXPECT_FLOAT_EQ(expected, value); -} - -TEST(CircleMPQSolverErrorApproximatorTest, verifyResultsTest_NEG) -{ - NConvGraph g; - g.init(); - - auto value = mpqsolver::bisection::approximate(g._input); - float expected = 0.f; - EXPECT_FLOAT_EQ(expected, value); - - value = mpqsolver::bisection::approximate(g._output); - expected = 0.f; - EXPECT_FLOAT_EQ(expected, value); -} diff --git a/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.cpp b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.cpp index 822df89..ee6376a 100644 --- a/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.cpp +++ b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.cpp @@ -17,6 +17,7 @@ #include "VISQErrorApproximator.h" #include +#include using namespace mpqsolver::bisection; @@ -24,16 +25,30 @@ void VISQErrorApproximator::init(const std::string &visq_data_path) { // read file std::ifstream file(visq_data_path); - if (!init(file)) - { - throw std::runtime_error("Invalid visq file " + visq_data_path); - } + init(file); } -bool VISQErrorApproximator::init(std::istream &) +void VISQErrorApproximator::init(std::istream &visq_data) { - // TODO - return true; + Json::Reader reader; + Json::Value completeJsonData; + if (!reader.parse(visq_data, completeJsonData)) + { + throw std::runtime_error("Invalid visq stream"); + } + + if (!completeJsonData.isMember("error")) + { + throw std::runtime_error("No 'error' section in visq stream"); + } + + auto layers = completeJsonData["error"][0]; + auto names = layers.getMemberNames(); + for (auto name : names) + { + auto value = layers[name].asFloat(); + _layer_errors[name] = value; + } } float VISQErrorApproximator::approximate(const std::string &node_name) const diff --git a/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.h b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.h index 855bca8..0d963ce 100644 --- a/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.h +++ b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.h @@ -45,9 +45,9 @@ public: private: /** - * @brief initiliaze by visq_data (returns success) + * @brief initiliaze by visq_data (throws on failure) */ - bool init(std::istream &visq_data); + void init(std::istream &visq_data); private: std::string _visq_data_path; diff --git a/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.test.cpp b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.test.cpp new file mode 100644 index 0000000..ccacb1a --- /dev/null +++ b/compiler/circle-mpqsolver/src/bisection/VISQErrorApproximator.test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VISQErrorApproximator.h" + +#include +#include +#include + +namespace +{ + +void writeDataToFile(const std::string &path, const std::string &data) +{ + std::ofstream file; + file.open(path); + file << data; + file.close(); +} + +void makeTemporaryFile(char *name_template) +{ + int fd = mkstemp(name_template); + if (fd == -1) + { + throw std::runtime_error{"mkstemp failed"}; + } +} + +} // namespace + +TEST(CircleMPQSolverVISQErrorApproximatorTest, verifyResultsTest) +{ + static std::string errors_key = "error"; + static std::string layer_key = "layer_0"; + static float layer_error = 0.5f; + // trivial json with a single layer + Json::Value error_data; + Json::Value layer_data; + layer_data[layer_key] = layer_error; + error_data[errors_key].append(layer_data); + + Json::StreamWriterBuilder builder; + auto data = Json::writeString(builder, error_data); + + char path[] = "VISQErrorApproximator-TEST-XXXXXX"; + makeTemporaryFile(path); + writeDataToFile(path, data); + + mpqsolver::bisection::VISQErrorApproximator approximator; + EXPECT_NO_THROW(approximator.init(path)); + EXPECT_FLOAT_EQ(approximator.approximate(layer_key), layer_error); + unlink(path); +} + +TEST(CircleMPQSolverVISQErrorApproximatorTest, verifyResultsTest_NEG) +{ + Json::Value error_data; + // just an empty json + Json::StreamWriterBuilder builder; + auto data = Json::writeString(builder, error_data); + + char path[] = "VISQErrorApproximator-TEST-NEG-XXXXXX"; + makeTemporaryFile(path); + writeDataToFile(path, data); + + mpqsolver::bisection::VISQErrorApproximator approximator; + EXPECT_THROW(approximator.init(path), std::exception); + unlink(path); +} diff --git a/compiler/circle-mpqsolver/src/core/Dumper.cpp b/compiler/circle-mpqsolver/src/core/Dumper.cpp new file mode 100644 index 0000000..3a94cb3 --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/Dumper.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dumper.h" + +#include +#include + +#include +#include +#include + +using namespace mpqsolver::core; + +namespace +{ + +const std::string default_dtype_key = "default_quantization_dtype"; +const std::string default_granularity_key = "default_granularity"; +const std::string layers_key = "layers"; +const std::string model_key = "model_path"; +const std::string layer_name_key = "name"; +const std::string layer_dtype_key = "dtype"; +const std::string layer_granularity_key = "granularity"; + +} // namespace + +Dumper::Dumper(const std::string &dir_path) : _dir_path(dir_path) {} + +void Dumper::set_model_path(const std::string &model_path) { _model_path = model_path; } + +void Dumper::dump_MPQ_configuration(const LayerParams &layers, const std::string &def_dtype, + const std::string &path) const +{ + Json::Value mpq_data; + mpq_data[default_dtype_key] = def_dtype; + mpq_data[default_granularity_key] = "channel"; + mpq_data[model_key] = _model_path; + + Json::Value layers_data; + for (auto &layer : layers) + { + Json::Value layer_data; + layer_data[layer_name_key] = layer->name; + layer_data[layer_granularity_key] = layer->granularity; + layer_data[layer_dtype_key] = layer->dtype; + layers_data.append(layer_data); + } + mpq_data[layers_key] = layers_data; + + Json::StreamWriterBuilder builder; + auto data = Json::writeString(builder, mpq_data); + + write_data_to_file(path, data); +} + +void Dumper::prepare_directory(const std::string &dir_path) const +{ + struct stat sb; + if (stat(dir_path.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) + { + if (mkdir(dir_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + throw std::runtime_error("Failed to create directory for dumping intermediate results"); + } + } +} + +void Dumper::dump_MPQ_configuration(const LayerParams &layers, const std::string &def_dtype, + int step) const +{ + prepare_directory(_dir_path); + std::string path = _dir_path + "/Configuration_" + std::to_string(step) + ".mpq.json"; + dump_MPQ_configuration(layers, def_dtype, path); +} + +void Dumper::dump_final_MPQ(const LayerParams &layers, const std::string &def_dtype) const +{ + prepare_directory(_dir_path); + std::string path = _dir_path + "/FinalConfiguration" + ".mpq.json"; + dump_MPQ_configuration(layers, def_dtype, path); +} + +void Dumper::write_data_to_file(const std::string &path, const std::string &data) const +{ + std::ofstream file; + file.open(path); + file << data; + file.close(); +} + +void Dumper::save_circle(luci::Module *module, std::string &path) const +{ + luci::CircleExporter exporter; + luci::CircleFileExpContract contract(module, path); + if (!exporter.invoke(&contract)) + { + throw std::runtime_error("Failed to export circle model to " + path); + } +} + +void Dumper::dump_quantized(luci::Module *module, uint32_t step) const +{ + std::string path = _dir_path + "/quantized_" + std::to_string(step) + ".mpq.circle"; + save_circle(module, path); +} + +void Dumper::dump_error(float error, const std::string &tag, const std::string &path) const +{ + std::ofstream file; + file.open(path, std::ios_base::app); + file << tag << " " << error << std::endl; + file.close(); +} + +void Dumper::prepare_for_error_dumping() const +{ + prepare_directory(_dir_path); + std::string path = get_error_path(); + std::ofstream file; + file.open(path); // create empty + file.close(); +} + +void Dumper::dump_Q8_error(float error) const +{ + std::string path = get_error_path(); + dump_error(error, "Q8", path); +} + +void Dumper::dump_Q16_error(float error) const +{ + std::string path = get_error_path(); + dump_error(error, "Q16", path); +} + +void Dumper::dump_MPQ_error(float error, uint32_t step) const +{ + std::string path = get_error_path(); + dump_error(error, std::to_string(step), path); +} + +void Dumper::dump_MPQ_error(float error) const +{ + std::string path = get_error_path(); + dump_error(error, "FINAL", path); +} diff --git a/compiler/circle-mpqsolver/src/core/Dumper.h b/compiler/circle-mpqsolver/src/core/Dumper.h new file mode 100644 index 0000000..220b54a --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/Dumper.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MPQSOLVER_DUMPER_H__ +#define __MPQSOLVER_DUMPER_H__ + +#include +#include + +#include + +namespace mpqsolver +{ +namespace core +{ + +using LayerParam = luci::CircleQuantizer::Options::LayerParam; +using LayerParams = std::vector>; + +class Dumper final +{ +public: + Dumper() = default; + Dumper(const std::string &dir_path); + + /** + * @brief sets model path for further usage + */ + void set_model_path(const std::string &model_path); + + /** + * @brief dumps mpq configuration + * @param layers specific quantization parameters + * @param def_dtype default quantization data type + * @param step id of mpq configuration + */ + void dump_MPQ_configuration(const LayerParams &layers, const std::string &def_dtype, + int step) const; + + /** + * @brief dumps final mpq configuration + * @param layers specific quantization parameters + * @param def_dtype default quantization data type + */ + void dump_final_MPQ(const LayerParams &layers, const std::string &def_dtype) const; + + /** + * @brief dumps quantized module + * @param layers specific quantization parameters + * @param step id of quantized module + */ + void dump_quantized(luci::Module *module, uint32_t step) const; + + /** + * @brief create file for error dumping + */ + void prepare_for_error_dumping() const; + + /** + * @brief append error of Q8 quantization + */ + void dump_Q8_error(float error) const; + + /** + * @brief append error of Q16 quantization + */ + void dump_Q16_error(float error) const; + + /** + * @brief append error of mpq quantization + * @param error error of quantization + * @param step id of error + */ + void dump_MPQ_error(float error, uint32_t step) const; + + /** + * @brief dump final error + * @param error final error of quantization + */ + void dump_MPQ_error(float error) const; + +private: + void write_data_to_file(const std::string &path, const std::string &data) const; + void dump_MPQ_configuration(const LayerParams &layers, const std::string &def_dtype, + const std::string &path) const; + void prepare_directory(const std::string &dir_path) const; + void save_circle(luci::Module *module, std::string &path) const; + void dump_error(float error, const std::string &tag, const std::string &path) const; + std::string get_error_path() const { return _dir_path + "/errors" + ".mpq.txt"; } + +private: + std::string _dir_path; + std::string _model_path; + +}; // Dumper + +} // namespace core +} // namespace mpqsolver + +#endif //__MPQSOLVER_DUMPER_H__ diff --git a/compiler/circle-mpqsolver/src/core/DumpingHooks.cpp b/compiler/circle-mpqsolver/src/core/DumpingHooks.cpp new file mode 100644 index 0000000..4d0522b --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/DumpingHooks.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DumpingHooks.h" + +using namespace mpqsolver::core; + +DumpingHooks::DumpingHooks(const std::string &save_path) + : _save_path(save_path), _dumper(_save_path) +{ +} + +void DumpingHooks::on_begin_solver(const std::string &model_path, float q8error, float q16error) +{ + _model_path = model_path; + _dumper.set_model_path(_model_path); + _dumper.prepare_for_error_dumping(); + _dumper.dump_Q8_error(q8error); + _dumper.dump_Q16_error(q16error); +} + +void DumpingHooks::on_begin_iteration() +{ + _in_iterations = true; + _num_of_iterations += 1; +} + +void DumpingHooks::on_end_iteration(const LayerParams &layers, const std::string &def_type, + float error) const +{ + _dumper.dump_MPQ_configuration(layers, def_type, _num_of_iterations); + _dumper.dump_MPQ_error(error, _num_of_iterations); +} + +void DumpingHooks::on_end_solver(const LayerParams &layers, const std::string &def_dtype, + float qerror) +{ + _dumper.dump_final_MPQ(layers, def_dtype); + _dumper.dump_MPQ_error(qerror); + _in_iterations = false; +} + +void DumpingHooks::on_quantized(luci::Module *module) const +{ + if (_in_iterations) + { + _dumper.dump_quantized(module, _num_of_iterations); + } +} diff --git a/compiler/circle-mpqsolver/src/core/DumpingHooks.h b/compiler/circle-mpqsolver/src/core/DumpingHooks.h new file mode 100644 index 0000000..c432a9a --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/DumpingHooks.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MPQSOLVER_DUMPING_HOOKS_H__ +#define __MPQSOLVER_DUMPING_HOOKS_H__ + +#include + +#include +#include +#include + +#include + +namespace mpqsolver +{ +namespace core +{ + +/** + * @brief DumpingHooks is intended to save intermediate results + */ +class DumpingHooks final : public QuantizerHook, public SolverHooks +{ +public: + /** + * @brief DumpingHooks constructor + * @param save_path directory where all intermediate data will be saved + */ + DumpingHooks(const std::string &save_path); + + /** + * @brief called on successfull quantization + */ + virtual void on_quantized(luci::Module *module) const override; + + /** + * @brief called on the start of iterative search + */ + virtual void on_begin_solver(const std::string &model_path, float q8error, + float q16error) override; + + /** + * @brief called on the start of current iteration + */ + virtual void on_begin_iteration() override; + + /** + * @brief called at the end of current iteration + */ + virtual void on_end_iteration(const LayerParams &layers, const std::string &def_dtype, + float error) const override; + + /** + * @brief called at the end of iterative search + */ + virtual void on_end_solver(const LayerParams &layers, const std::string &def_dtype, + float qerror) override; + +protected: + std::string _model_path; + std::string _save_path; + Dumper _dumper; + uint32_t _num_of_iterations = 0; + bool _in_iterations = false; +}; + +} // namespace core +} // namespace mpqsolver + +#endif //__MPQSOLVER_DUMPING_HOOKS_H__ diff --git a/compiler/circle-mpqsolver/src/bisection/ErrorMetric.cpp b/compiler/circle-mpqsolver/src/core/ErrorMetric.cpp similarity index 94% rename from compiler/circle-mpqsolver/src/bisection/ErrorMetric.cpp rename to compiler/circle-mpqsolver/src/core/ErrorMetric.cpp index 74c7fd6..23ddfcb 100644 --- a/compiler/circle-mpqsolver/src/bisection/ErrorMetric.cpp +++ b/compiler/circle-mpqsolver/src/core/ErrorMetric.cpp @@ -22,7 +22,7 @@ #include #include -using namespace mpqsolver::bisection; +using namespace mpqsolver::core; /** * @brief compare first and second operands in MAE (Mean Average Error metric) @@ -56,5 +56,10 @@ float MAEMetric::compute(const WholeOutput &first, const WholeOutput &second) co } } + if (output_size == 0) + { + throw std::runtime_error("nothing to compare"); + } + return error / output_size; } diff --git a/compiler/circle-mpqsolver/src/bisection/ErrorMetric.h b/compiler/circle-mpqsolver/src/core/ErrorMetric.h similarity index 87% rename from compiler/circle-mpqsolver/src/bisection/ErrorMetric.h rename to compiler/circle-mpqsolver/src/core/ErrorMetric.h index 9ef8662..fc53539 100644 --- a/compiler/circle-mpqsolver/src/bisection/ErrorMetric.h +++ b/compiler/circle-mpqsolver/src/core/ErrorMetric.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef __MPQSOLVER_BISECTION_ERROR_METRIC_H__ -#define __MPQSOLVER_BISECTION_ERROR_METRIC_H__ +#ifndef __MPQSOLVER_CORE_ERROR_METRIC_H__ +#define __MPQSOLVER_CORE_ERROR_METRIC_H__ #include namespace mpqsolver { -namespace bisection +namespace core { using Buffer = std::vector; @@ -49,7 +49,7 @@ public: float compute(const WholeOutput &first, const WholeOutput &second) const; }; -} // namespace bisection +} // namespace core } // namespace mpqsolver -#endif //__MPQSOLVER_BISECTION_ERROR_METRIC_H__ +#endif //__MPQSOLVER_CORE_ERROR_METRIC_H__ diff --git a/compiler/circle-mpqsolver/src/core/ErrorMetric.test.cpp b/compiler/circle-mpqsolver/src/core/ErrorMetric.test.cpp new file mode 100644 index 0000000..232d9bc --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/ErrorMetric.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ErrorMetric.h" + +#include + +TEST(CircleMPQSolverMAEMetricTest, verifyResultsTest) +{ + size_t num_elements = 512; + mpqsolver::core::WholeOutput target, source; + // let target be zero + { + std::vector float_buffer(num_elements, 0.f); + auto const char_buffer = reinterpret_cast(float_buffer.data()); + auto const char_buffer_size = num_elements * sizeof(float) / sizeof(char); + std::vector buffer(char_buffer, char_buffer + char_buffer_size); + + mpqsolver::core::Output out = mpqsolver::core::Output(1, buffer); + target = mpqsolver::core::WholeOutput(1, out); + } + + // let source be one + { + std::vector float_buffer(num_elements, 1.f); + auto const char_buffer = reinterpret_cast(float_buffer.data()); + auto const char_buffer_size = num_elements * sizeof(float) / sizeof(char); + std::vector buffer(char_buffer, char_buffer + char_buffer_size); + mpqsolver::core::Output out = mpqsolver::core::Output(1, buffer); + source = mpqsolver::core::WholeOutput(1, out); + } + + mpqsolver::core::MAEMetric metric; + float value = metric.compute(target, source); + EXPECT_FLOAT_EQ(value, 1.f); +} + +TEST(CircleMPQSolverMAEMetricTest, verifyResultsTest_NEG) +{ + mpqsolver::core::MAEMetric metric; + mpqsolver::core::WholeOutput target, source; + EXPECT_ANY_THROW(metric.compute(target, source)); +} diff --git a/compiler/circle-mpqsolver/src/bisection/Evaluator.cpp b/compiler/circle-mpqsolver/src/core/Evaluator.cpp similarity index 97% rename from compiler/circle-mpqsolver/src/bisection/Evaluator.cpp rename to compiler/circle-mpqsolver/src/core/Evaluator.cpp index 94d46a3..c7afda5 100644 --- a/compiler/circle-mpqsolver/src/bisection/Evaluator.cpp +++ b/compiler/circle-mpqsolver/src/core/Evaluator.cpp @@ -20,7 +20,7 @@ #include -using namespace mpqsolver::bisection; +using namespace mpqsolver::core; using Shape = std::vector; @@ -69,12 +69,13 @@ WholeOutput compute_outputs(const luci::Module *module, const std::string &h5fil { loco::DataType dtype; Shape shape; - importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data()); + importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), + input_data.size()); } else { // Skip type/shape check for raw data - importer.readTensor(record_idx, input_idx, input_data.data()); + importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); } interpreter.writeInputTensor(input_node, input_data.data(), input_data.size()); diff --git a/compiler/circle-mpqsolver/src/bisection/Evaluator.h b/compiler/circle-mpqsolver/src/core/Evaluator.h similarity index 89% rename from compiler/circle-mpqsolver/src/bisection/Evaluator.h rename to compiler/circle-mpqsolver/src/core/Evaluator.h index 144c86c..9820508 100644 --- a/compiler/circle-mpqsolver/src/bisection/Evaluator.h +++ b/compiler/circle-mpqsolver/src/core/Evaluator.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __MPQSOLVER_BISECTION_EVALUATOR_H__ -#define __MPQSOLVER_BISECTION_EVALUATOR_H__ +#ifndef __MPQSOLVER_CORE_EVALUATOR_H__ +#define __MPQSOLVER_CORE_EVALUATOR_H__ #include "ErrorMetric.h" @@ -27,7 +27,7 @@ namespace mpqsolver { -namespace bisection +namespace core { class DatasetEvaluator final @@ -60,7 +60,7 @@ private: const ErrorMetric *_metric = nullptr; }; -} // namespace bisection +} // namespace core } // namespace mpqsolver -#endif //__MPQSOLVER_BISECTION_EVALUATOR_H__ +#endif //__MPQSOLVER_CORE_EVALUATOR_H__ diff --git a/compiler/circle-mpqsolver/src/bisection/Quantizer.cpp b/compiler/circle-mpqsolver/src/core/Quantizer.cpp similarity index 95% rename from compiler/circle-mpqsolver/src/bisection/Quantizer.cpp rename to compiler/circle-mpqsolver/src/core/Quantizer.cpp index 6fe1d56..4217931 100644 --- a/compiler/circle-mpqsolver/src/bisection/Quantizer.cpp +++ b/compiler/circle-mpqsolver/src/core/Quantizer.cpp @@ -19,7 +19,7 @@ #include -using namespace mpqsolver::bisection; +using namespace mpqsolver::core; using AlgorithmParameters = luci::CircleQuantizer::Options::AlgorithmParameters; using Algorithms = luci::CircleQuantizer::Options::Algorithm; @@ -54,6 +54,8 @@ Quantizer::Quantizer(const std::string &input_dtype, const std::string &output_d { } +void Quantizer::set_hook(const QuantizerHook *hook) { _hook = hook; } + /** * @brief quantize recorded module (min/max initialized) with specified parameters * returns true on success @@ -104,6 +106,11 @@ bool Quantizer::quantize(luci::Module *module, const std::string &quant_dtype, } } + if (_hook) + { + _hook->on_quantized(module); + } + return true; } diff --git a/compiler/circle-mpqsolver/src/bisection/Quantizer.h b/compiler/circle-mpqsolver/src/core/Quantizer.h similarity index 76% rename from compiler/circle-mpqsolver/src/bisection/Quantizer.h rename to compiler/circle-mpqsolver/src/core/Quantizer.h index ceaabf5..259d5c4 100644 --- a/compiler/circle-mpqsolver/src/bisection/Quantizer.h +++ b/compiler/circle-mpqsolver/src/core/Quantizer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __MPQSOLVER_BISECTION_QUANTIZER_H__ -#define __MPQSOLVER_BISECTION_QUANTIZER_H__ +#ifndef __MPQSOLVER_CORE_QUANTIZER_H__ +#define __MPQSOLVER_CORE_QUANTIZER_H__ #include #include @@ -26,18 +26,32 @@ namespace mpqsolver { -namespace bisection +namespace core { using LayerParam = luci::CircleQuantizer::Options::LayerParam; using LayerParams = std::vector>; +struct QuantizerHook +{ + /** + * @brief called on successfull quantization + * @param module quantized module + */ + virtual void on_quantized(luci::Module *module) const = 0; +}; + class Quantizer { public: Quantizer(const std::string &input_dtype, const std::string &output_type); /** + * @brief set hook on the end of quantization event + */ + void set_hook(const QuantizerHook *callback); + + /** * @brief quantize recorded module (min/max initialized) with specified parameters * returns true on success */ @@ -53,9 +67,10 @@ public: private: std::string _input_dtype = "uint8"; std::string _output_dtype = "uint8"; + const QuantizerHook *_hook = nullptr; }; -} // namespace bisection +} // namespace core } // namespace mpqsolver -#endif //__MPQSOLVER_BISECTION_QUANTIZER_H__ +#endif //__MPQSOLVER_CORE_QUANTIZER_H__ diff --git a/compiler/circle-mpqsolver/src/bisection/Quantizer.test.cpp b/compiler/circle-mpqsolver/src/core/Quantizer.test.cpp similarity index 92% rename from compiler/circle-mpqsolver/src/bisection/Quantizer.test.cpp rename to compiler/circle-mpqsolver/src/core/Quantizer.test.cpp index 8575156..7d7e74f 100644 --- a/compiler/circle-mpqsolver/src/bisection/Quantizer.test.cpp +++ b/compiler/circle-mpqsolver/src/core/Quantizer.test.cpp @@ -85,8 +85,8 @@ TEST(CircleMPQSolverQuantizerTest, verifyResultsTest) g.transfer_to(m.get()); std::string def_quant = "uint8"; - mpqsolver::bisection::Quantizer quantizer(def_quant, def_quant); - mpqsolver::bisection::LayerParams params; + mpqsolver::core::Quantizer quantizer(def_quant, def_quant); + mpqsolver::core::LayerParams params; auto res = quantizer.quantize(m.get(), def_quant, params); EXPECT_TRUE(res); auto quant_param = add->quantparam(); @@ -100,8 +100,8 @@ TEST(CircleMPQSolverQuantizerTest, verifyResultsTest) TEST(CircleMPQSolverQuantizerTest, verifyResultsTest_NEG) { std::string def_quant = "uint8"; - mpqsolver::bisection::Quantizer quantizer(def_quant, def_quant); - mpqsolver::bisection::LayerParams params; + mpqsolver::core::Quantizer quantizer(def_quant, def_quant); + mpqsolver::core::LayerParams params; auto res = quantizer.quantize(nullptr, def_quant, params); EXPECT_TRUE(!res); } diff --git a/onert-micro/luci-interpreter/pal/linux/PALFill.h b/compiler/circle-mpqsolver/src/core/SolverHooks.cpp similarity index 64% rename from onert-micro/luci-interpreter/pal/linux/PALFill.h rename to compiler/circle-mpqsolver/src/core/SolverHooks.cpp index 212350a..bbe1bb4 100644 --- a/onert-micro/luci-interpreter/pal/linux/PALFill.h +++ b/compiler/circle-mpqsolver/src/core/SolverHooks.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +14,4 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_FILL_H -#define LUCI_INTERPRETER_PAL_FILL_H - -#include "tensorflow/lite/kernels/internal/reference/reference_ops.h" - -#endif // LUCI_INTERPRETER_PAL_FILL_H +#include "SolverHooks.h" diff --git a/compiler/circle-mpqsolver/src/core/SolverHooks.h b/compiler/circle-mpqsolver/src/core/SolverHooks.h new file mode 100644 index 0000000..851a699 --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/SolverHooks.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MPQSOLVER_SOLVER_HOOKS_H__ +#define __MPQSOLVER_SOLVER_HOOKS_H__ + +#include + +#include + +#include + +namespace mpqsolver +{ +namespace core +{ + +class SolverHooks +{ +public: + /** + * @brief called on the start of iterative search + * @param model_path path of original float model to quantize + * @param q8error error of Q8 quantization + * @param q16error error of Q16 quantization + */ + virtual void on_begin_solver(const std::string &model_path, float q8error, float q16error) = 0; + + /** + * @brief called on the start of current iteration + */ + virtual void on_begin_iteration() = 0; + + /** + * @brief called at the end of current iteration + * @param layers model nodes with specific quantization parameters + * @param def_dtype default quantization dtype + * @param error error of quantization for current iteration + */ + virtual void on_end_iteration(const LayerParams &layers, const std::string &def_dtype, + float error) const = 0; + + /** + * @brief called at the end of iterative search + * @param layers model nodes with specific quantization parameters + * @param def_dtype default quantization dtype + * @param qerror final error of quantization + */ + virtual void on_end_solver(const LayerParams &layers, const std::string &def_dtype, + float qerror) = 0; +}; + +} // namespace core +} // namespace mpqsolver + +#endif //__MPQSOLVER_SOLVER_HOOKS_H__ diff --git a/compiler/circle-mpqsolver/src/core/SolverOutput.cpp b/compiler/circle-mpqsolver/src/core/SolverOutput.cpp new file mode 100644 index 0000000..c14fe30 --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/SolverOutput.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SolverOutput.h" + +#include + +SolverOutput &SolverOutput::get(void) +{ + static SolverOutput d; + return d; +} + +const SolverOutput &SolverOutput::operator<<(const std::string &message) const +{ + if (_turn_on) + { + std::cout << message; + } + + return *this; +} + +const SolverOutput &SolverOutput::operator<<(float value) const +{ + if (_turn_on) + { + std::cout << value; + } + + return *this; +} + +void SolverOutput::TurnOn(bool on) { _turn_on = on; } diff --git a/compiler/circle-mpqsolver/src/core/SolverOutput.h b/compiler/circle-mpqsolver/src/core/SolverOutput.h new file mode 100644 index 0000000..218d3b9 --- /dev/null +++ b/compiler/circle-mpqsolver/src/core/SolverOutput.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MPQSOLVER_SOLVER_OUTPUT_H__ +#define __MPQSOLVER_SOLVER_OUTPUT_H__ + +#include + +/** + * @brief SolverOutput prints important performance information + */ +class SolverOutput +{ +private: + /** + * @brief construct SolverOutput + */ + SolverOutput() = default; + +public: + /** + * @brief get singleton object + */ + static SolverOutput &get(void); + + /** + * @brief print string message + */ + const SolverOutput &operator<<(const std::string &message) const; + + /** + * @brief print float value + */ + const SolverOutput &operator<<(float value) const; + + /** + * @brief turn on/off actual output + */ + void TurnOn(bool on); + +private: + bool _turn_on = true; +}; + +#endif // __MPQSOLVER_SOLVER_OUTPUT_H__ diff --git a/compiler/circle-mpqsolver/src/bisection/TestHelper.h b/compiler/circle-mpqsolver/src/core/TestHelper.h similarity index 100% rename from compiler/circle-mpqsolver/src/bisection/TestHelper.h rename to compiler/circle-mpqsolver/src/core/TestHelper.h diff --git a/compiler/circle-operator/CMakeLists.txt b/compiler/circle-operator/CMakeLists.txt index 6817a86..33d9a96 100644 --- a/compiler/circle-operator/CMakeLists.txt +++ b/compiler/circle-operator/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_circle04) +if(NOT TARGET mio_circle06) return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) set(DRIVER "driver/Driver.cpp") @@ -10,8 +10,8 @@ add_executable(circle-operator ${DRIVER} ${SOURCES}) target_include_directories(circle-operator PRIVATE src) target_link_libraries(circle-operator arser) target_link_libraries(circle-operator foder) -target_link_libraries(circle-operator mio_circle04) -target_link_libraries(circle-operator mio_circle04_helper) +target_link_libraries(circle-operator mio_circle06) +target_link_libraries(circle-operator mio_circle06_helper) target_link_libraries(circle-operator safemain) install(TARGETS circle-operator DESTINATION bin) diff --git a/compiler/circle-operator/requires.cmake b/compiler/circle-operator/requires.cmake index 183dfe2..b3a2638 100644 --- a/compiler/circle-operator/requires.cmake +++ b/compiler/circle-operator/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-circle04") +require("mio-circle06") require("safemain") diff --git a/compiler/circle-opselector/driver/Driver.cpp b/compiler/circle-opselector/driver/Driver.cpp index 6549fc7..5ad2b9c 100644 --- a/compiler/circle-opselector/driver/Driver.cpp +++ b/compiler/circle-opselector/driver/Driver.cpp @@ -102,7 +102,11 @@ int entry(int argc, char **argv) new_module = op_selector.select_by(operator_input); } - assert(opselector::exportModule(new_module.get(), output_path)); + if (not opselector::exportModule(new_module.get(), output_path)) + { + std::cerr << "ERROR: Cannot export the module" << std::endl; + return EXIT_FAILURE; + } return 0; } diff --git a/compiler/circle-opselector/src/OpSelector.cpp b/compiler/circle-opselector/src/OpSelector.cpp index 31cbf9c..09a6654 100644 --- a/compiler/circle-opselector/src/OpSelector.cpp +++ b/compiler/circle-opselector/src/OpSelector.cpp @@ -156,6 +156,10 @@ std::unique_ptr make_graph(const std::vectorgraph(); + const auto original_outputs = loco::output_nodes(const_cast(original_graph)); + // set graph output for (auto &n : nodes) { @@ -169,8 +173,19 @@ std::unique_ptr make_graph(const std::vector(const std::vector &tokens) auto cnode = loco::must_cast(node); std::string node_name = cnode->name(); - for (auto selected_name : tokens) + for (const auto &selected_name : tokens) if (selected_name.compare(node_name) == 0) // find the selected name selected_nodes.emplace_back(cnode); } diff --git a/compiler/circle-part-value-test/CMakeLists.txt b/compiler/circle-part-value-test/CMakeLists.txt index c4bd200..04ad830 100644 --- a/compiler/circle-part-value-test/CMakeLists.txt +++ b/compiler/circle-part-value-test/CMakeLists.txt @@ -107,17 +107,7 @@ add_dependencies(circle_part_value_test_prepare common_artifacts_deps) add_test(NAME circle_part_value_test COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/part_eval_all.sh" "${CMAKE_CURRENT_BINARY_DIR}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" "$" ${PARTITION_LIST} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test(NAME circle_part_value_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/part_eval_all.sh" - "${CMAKE_CURRENT_BINARY_DIR}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - "$" - ${PARTITION_LIST} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt b/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt index a9420d4..adb2c0f 100644 --- a/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt +++ b/compiler/circle-quantizer-dredd-recipe-test/CMakeLists.txt @@ -82,6 +82,25 @@ macro(AddFakeQuant RECIPE) list(APPEND TEST_NAMES ${RECIPE}) endmacro(AddFakeQuant) +# Macro to generate re-quantized models +macro(AddReQuant RECIPE) + set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE}.circle") + # NOTE We use .q.circle because it is convention for output file (see testall.sh for more details) + set(REQUANT_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.q.circle") + + # Generate re-quantized .circle + add_custom_command(OUTPUT ${REQUANT_CIRCLE_PATH} + COMMAND $ --requantize int8 uint8 ${CIRCLE_PATH} ${REQUANT_CIRCLE_PATH} + DEPENDS + circle-quantizer + ${CIRCLE_PATH} + COMMENT "Generate ${RECIPE}.q.circle" + ) + + list(APPEND TEST_DEPS ${REQUANT_CIRCLE_PATH}) + list(APPEND TEST_NAMES ${RECIPE}) +endmacro(AddReQuant) + # Macro to quantize without quantize_dequantize_weights macro(AddSkipQDQW RECIPE) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) diff --git a/compiler/circle-quantizer-dredd-recipe-test/test.lst b/compiler/circle-quantizer-dredd-recipe-test/test.lst index 1464083..309069b 100644 --- a/compiler/circle-quantizer-dredd-recipe-test/test.lst +++ b/compiler/circle-quantizer-dredd-recipe-test/test.lst @@ -73,6 +73,18 @@ AddFakeQuant(Quant_Add_000) AddFakeQuant(Quant_DepthToSpace_000) AddFakeQuant(Quant_SpaceToDepth_000) +# Requantize Test (I8 -> U8) +AddReQuant(Quant_Add_I8_000) +AddReQuant(Quant_AveragePool2D_I8_000) +AddReQuant(Quant_Conv_I8_000) +AddReQuant(Quant_DepthwiseConv2D_I8_000) +AddReQuant(Quant_MaxPool2D_I8_000) +AddReQuant(Quant_Mean_I8_000) +AddReQuant(Quant_Mul_I8_000) +AddReQuant(Quant_PRelu_I8_000) +AddReQuant(Quant_ReLU_I8_000) +AddReQuant(Quant_TransposeConv_I8_000) + ## CIRCLE RECIPE # MPQ Test (default: u8, target: s16) diff --git a/compiler/circle-quantizer/src/CircleQuantizer.cpp b/compiler/circle-quantizer/src/CircleQuantizer.cpp index 33a845c..02b96f9 100644 --- a/compiler/circle-quantizer/src/CircleQuantizer.cpp +++ b/compiler/circle-quantizer/src/CircleQuantizer.cpp @@ -94,6 +94,7 @@ void print_exclusive_options(void) std::cout << " --quantize_with_minmax" << std::endl; std::cout << " --requantize" << std::endl; std::cout << " --force_quantparam" << std::endl; + std::cout << " --quantize_weights" << std::endl; } void print_version(void) @@ -115,6 +116,7 @@ int entry(int argc, char **argv) const std::string fq = "--force_quantparam"; const std::string cq = "--copy_quantparam"; const std::string fake_quant = "--fake_quantize"; + const std::string qw = "--quantize_weights"; const std::string cfg = "--config"; const std::string tf_maxpool = "--TF-style_maxpool"; @@ -174,6 +176,13 @@ int entry(int argc, char **argv) "Two arguments required: source_tensor_name(string), " "destination_tensor_name(string)"); + arser.add_argument(qw) + .nargs(3) + .type(arser::DataType::STR_VEC) + .help("Quantize weights values only" + "Three arguments required: input_model_dtype(float32) " + "output_model_dtype(int8, int16) granularity(channel)"); + arser.add_argument("--input_type") .help("Input type of quantized model (uint8, int16, int32, int64, float32, or bool). For " "multiple inputs, " @@ -204,13 +213,14 @@ int entry(int argc, char **argv) } { - // only one of qdqw, qwmm, rq, fq, cq, fake_quant option can be used + // only one of qdqw, qwmm, rq, fq, cq, fake_quant, qw option can be used int32_t opt_used = arser[qdqw] ? 1 : 0; opt_used += arser[qwmm] ? 1 : 0; opt_used += arser[rq] ? 1 : 0; opt_used += arser[fq] ? 1 : 0; opt_used += arser[cq] ? 1 : 0; opt_used += arser[fake_quant] ? 1 : 0; + opt_used += arser[qw] ? 1 : 0; if (opt_used != 1) { print_exclusive_options(); @@ -368,6 +378,21 @@ int entry(int argc, char **argv) if (arser[fake_quant]) options->enable(Algorithms::ConvertToFakeQuantizedModel); + if (arser[qw]) + { + auto values = arser.get>(qw); + if (values.size() != 3) + { + std::cerr << arser; + return 255; + } + options->enable(Algorithms::QuantizeWeights); + + options->param(AlgorithmParameters::Quantize_input_model_dtype, values.at(0)); + options->param(AlgorithmParameters::Quantize_output_model_dtype, values.at(1)); + options->param(AlgorithmParameters::Quantize_granularity, values.at(2)); + } + std::string input_path = arser.get("input"); std::string output_path = arser.get("output"); diff --git a/compiler/circle-tensordump/CMakeLists.txt b/compiler/circle-tensordump/CMakeLists.txt index 676aecd..ed6ddc4 100644 --- a/compiler/circle-tensordump/CMakeLists.txt +++ b/compiler/circle-tensordump/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_circle04) +if(NOT TARGET mio_circle06) return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) nnas_find_package(HDF5 COMPONENTS STATIC QUIET) @@ -19,8 +19,8 @@ target_include_directories(circle-tensordump PRIVATE ${HDF5_INCLUDE_DIRS}) target_link_libraries(circle-tensordump PRIVATE ${HDF5_CXX_LIBRARIES}) target_link_libraries(circle-tensordump PRIVATE arser) target_link_libraries(circle-tensordump PRIVATE foder) -target_link_libraries(circle-tensordump PRIVATE mio_circle04) -target_link_libraries(circle-tensordump PRIVATE mio_circle04_helper) +target_link_libraries(circle-tensordump PRIVATE mio_circle06) +target_link_libraries(circle-tensordump PRIVATE mio_circle06_helper) target_link_libraries(circle-tensordump PRIVATE safemain) install(TARGETS circle-tensordump DESTINATION bin) diff --git a/compiler/circle-tensordump/requires.cmake b/compiler/circle-tensordump/requires.cmake index 183dfe2..b3a2638 100644 --- a/compiler/circle-tensordump/requires.cmake +++ b/compiler/circle-tensordump/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-circle04") +require("mio-circle06") require("safemain") diff --git a/compiler/circle-tensordump/src/Dump.cpp b/compiler/circle-tensordump/src/Dump.cpp index 49afa73..98cb5ae 100644 --- a/compiler/circle-tensordump/src/Dump.cpp +++ b/compiler/circle-tensordump/src/Dump.cpp @@ -185,6 +185,10 @@ H5::PredType hdf5_dtype_cast(const circle::TensorType &circle_type) { return H5::PredType::NATIVE_UINT8; } + case circle::TensorType_INT8: + { + return H5::PredType::NATIVE_INT8; + } case circle::TensorType_INT16: { return H5::PredType::NATIVE_INT16; diff --git a/compiler/circle-verify/CMakeLists.txt b/compiler/circle-verify/CMakeLists.txt index 5d0eb94..cdf74cc 100644 --- a/compiler/circle-verify/CMakeLists.txt +++ b/compiler/circle-verify/CMakeLists.txt @@ -1,14 +1,14 @@ -if(NOT TARGET mio_circle04) - message(STATUS "Skip circle-verify: mio_circle04 not found") +if(NOT TARGET mio_circle06) + message(STATUS "Skip circle-verify: mio_circle06 not found") return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(circle-verify ${SOURCES}) target_include_directories(circle-verify PRIVATE src) target_link_libraries(circle-verify arser) -target_link_libraries(circle-verify mio_circle04) +target_link_libraries(circle-verify mio_circle06) target_link_libraries(circle-verify safemain) target_link_libraries(circle-verify cwrap) target_link_libraries(circle-verify foder) diff --git a/compiler/circle-verify/requires.cmake b/compiler/circle-verify/requires.cmake index 74c8f44..2fd44ad 100644 --- a/compiler/circle-verify/requires.cmake +++ b/compiler/circle-verify/requires.cmake @@ -1,5 +1,5 @@ require("arser") -require("mio-circle04") +require("mio-circle06") require("safemain") require("cwrap") require("foder") diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst index 4fb1e78..2dd24af 100644 --- a/compiler/circle2circle-dredd-recipe-test/test.lst +++ b/compiler/circle2circle-dredd-recipe-test/test.lst @@ -54,6 +54,9 @@ Add(MaxPoolWithArgmax_001 PASS resolve_customop_max_pool_with_argmax) Add(MaxPoolWithArgmax_002 PASS resolve_customop_max_pool_with_argmax) Add(FullyConnected_007 PASS replace_non_const_fc_with_batch_matmul) Add(FullyConnected_008 PASS replace_non_const_fc_with_batch_matmul) +Add(Net_Gelu_000 PASS fuse_gelu) +Add(Net_Gelu_001 PASS fuse_gelu) +Add(HardSwish_001 PASS decompose_hardswish) ## CIRCLE RECIPE @@ -89,3 +92,6 @@ Add(REGRESS_ONNX_Conv_BN_MeanMean_001 PASS fuse_activation_function fuse_mean_with_mean fuse_transpose_with_mean) + +Add(REGRESS_ONNX_Mul_Mul_000 PASS + convert_nchw_to_nhwc) diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp index 9afb67d..6a7be22 100644 --- a/compiler/circle2circle/src/Circle2Circle.cpp +++ b/compiler/circle2circle/src/Circle2Circle.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -111,6 +112,7 @@ int entry(int argc, char **argv) add_switch(arser, "--fuse_preactivation_batchnorm", "This will fuse BatchNorm operators of pre-activations to Convolution operator"); add_switch(arser, "--fuse_prelu", "This will fuse operators to PReLU operator"); + add_switch(arser, "--fuse_gelu", "This will fuse operators to GeLU operator"); add_switch(arser, "--remove_duplicate_const", "This will remove all duplicate constant nodes"); add_switch(arser, "--remove_fakequant", "This will remove FakeQuant operators"); add_switch(arser, "--remove_quantdequant", "This will remove Quantize-Dequantize sequence"); @@ -167,11 +169,19 @@ int entry(int argc, char **argv) "Transform Minimum(6)-Maximum(0) pattern to Relu6 operator"); add_switch(arser, "--transform_min_relu_to_relu6", "Transform Minimum(6)-Relu pattern to Relu6 operator"); + add_switch(arser, "--decompose_hardswish", + "Decompose HardSwish operator to Add, Mul and Relu6 operators"); add_switch(arser, "--mute_warnings", "This will turn off warning messages"); add_switch(arser, "--disable_validation", "This will turn off operator validations. May help input model investigation."); add_switch(arser, "--generate_profile_data", "This will turn on profiling data generation."); + // Convert dynamic batch to single batch + // Users have to use this option only when the first dimension of rank 4 input (NHWC or NCHW) + // is dynamic. Remove this comment after non-rank 4 is supported. + add_switch(arser, "--dynamic_batch_to_single_batch", + "Convert dynamic batch size (first dimension) of inputs to 1."); + arser.add_argument("--change_outputs") .help("Experimental: Change first subgraph output nodes to CSV names"); @@ -257,6 +267,8 @@ int entry(int argc, char **argv) options->enable(Algorithms::FusePreActivationBatchNorm); if (arser.get("--fuse_prelu")) options->enable(Algorithms::FusePRelu); + if (arser.get("--fuse_gelu")) + options->enable(Algorithms::FuseGelu); if (arser.get("--fuse_transpose_with_mean")) options->enable(Algorithms::FuseTransposeWithMean); if (arser.get("--remove_duplicate_const")) @@ -313,6 +325,8 @@ int entry(int argc, char **argv) options->enable(Algorithms::TransformMinMaxToRelu6Pass); if (arser.get("--transform_min_relu_to_relu6")) options->enable(Algorithms::TransformMinReluToRelu6Pass); + if (arser.get("--decompose_hardswish")) + options->enable(Algorithms::DecomposeHardSwishPass); if (arser.get("--expand_broadcast_const")) options->enable(Algorithms::ExpandBroadcastConst); if (arser.get("--unroll_unidirseqlstm")) @@ -368,12 +382,40 @@ int entry(int argc, char **argv) csv_tokenize(csv_nodes, new_outputs); } + bool dynamic_batch_to_single_batch = false; + if (arser.get("--dynamic_batch_to_single_batch")) + { + dynamic_batch_to_single_batch = true; + } + // Import from input Circle file luci::ImporterEx importerex; auto module = importerex.importVerifyModule(input_path); if (module.get() == nullptr) return EXIT_FAILURE; + // Convert dynamic batch to single batch + // Why here? It has to be done before 'optimize', because most optimization + // passes are written based on static shapes + if (dynamic_batch_to_single_batch) + { + luci::dynamic_batch_to_single_batch(module.get()); + + if (!luci::validate_shape(module.get())) + { + if (settings->get(luci::UserSettings::Key::DisableValidation)) + std::cerr + << "WARNING: Invalid shape detected after converting dynamic batch to single batch" + << std::endl; + else + { + std::cerr << "ERROR: Invalid shape detected after converting dynamic batch to single batch" + << std::endl; + return 255; + } + } + } + if (change_outputs) { auto graph = module->graph(0); diff --git a/compiler/circlechef/CMakeLists.txt b/compiler/circlechef/CMakeLists.txt index b124d30..56c501c 100644 --- a/compiler/circlechef/CMakeLists.txt +++ b/compiler/circlechef/CMakeLists.txt @@ -5,10 +5,10 @@ if(NOT Protobuf_FOUND) return() endif(NOT Protobuf_FOUND) -if(NOT TARGET mio_circle04) - message(STATUS "circlechef: SKIP (missing mio-circle04)") +if(NOT TARGET mio_circle06) + message(STATUS "circlechef: SKIP (missing mio-circle06)") return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) # Recipe Parser add_subdirectory(proto) diff --git a/compiler/circlechef/circle/CMakeLists.txt b/compiler/circlechef/circle/CMakeLists.txt index 12dc721..cdd6040 100644 --- a/compiler/circlechef/circle/CMakeLists.txt +++ b/compiler/circlechef/circle/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(circlechef_circle STATIC ${SOURCES}) target_include_directories(circlechef_circle PUBLIC include) target_include_directories(circlechef_circle PRIVATE src) target_link_libraries(circlechef_circle circlechef_proto) -target_link_libraries(circlechef_circle mio_circle04) -target_link_libraries(circlechef_circle mio_circle04_helper) +target_link_libraries(circlechef_circle mio_circle06) +target_link_libraries(circlechef_circle mio_circle06_helper) target_link_libraries(circlechef_circle cwrap) target_link_libraries(circlechef_circle souschef) diff --git a/compiler/circlechef/core/CMakeLists.txt b/compiler/circlechef/core/CMakeLists.txt index 4159547..dc1dbc4 100644 --- a/compiler/circlechef/core/CMakeLists.txt +++ b/compiler/circlechef/core/CMakeLists.txt @@ -7,7 +7,7 @@ target_include_directories(circlechef_core PUBLIC include) target_include_directories(circlechef_core PRIVATE src) target_link_libraries(circlechef_core PUBLIC circlechef_proto) target_link_libraries(circlechef_core PUBLIC circlechef_log) -target_link_libraries(circlechef_core PUBLIC mio_circle04) +target_link_libraries(circlechef_core PUBLIC mio_circle06) target_link_libraries(circlechef_core PUBLIC souschef) target_link_libraries(circlechef_core PRIVATE nncc_coverage) diff --git a/compiler/circlechef/requires.cmake b/compiler/circlechef/requires.cmake index a5d6bed..67eaa27 100644 --- a/compiler/circlechef/requires.cmake +++ b/compiler/circlechef/requires.cmake @@ -1,7 +1,7 @@ require("arser") require("nnkit") require("cwrap") -require("mio-circle04") +require("mio-circle06") require("safemain") require("hermes") require("hermes-std") diff --git a/compiler/circledump/CMakeLists.txt b/compiler/circledump/CMakeLists.txt index 7485ff8..b732673 100644 --- a/compiler/circledump/CMakeLists.txt +++ b/compiler/circledump/CMakeLists.txt @@ -1,7 +1,7 @@ -if(NOT TARGET mio_circle04) - message(STATUS "Skip circledump: mio_circle04 not found") +if(NOT TARGET mio_circle06) + message(STATUS "Skip circledump: mio_circle06 not found") return() -endif(NOT TARGET mio_circle04) +endif(NOT TARGET mio_circle06) set(DRIVER "driver/Driver.cpp") @@ -11,8 +11,8 @@ add_executable(circledump ${DRIVER} ${SOURCES}) target_include_directories(circledump PRIVATE include) target_link_libraries(circledump arser) target_link_libraries(circledump foder) -target_link_libraries(circledump mio_circle04) -target_link_libraries(circledump mio_circle04_helper) +target_link_libraries(circledump mio_circle06) +target_link_libraries(circledump mio_circle06_helper) target_link_libraries(circledump safemain) install(TARGETS circledump DESTINATION bin) diff --git a/compiler/circledump/README.md b/compiler/circledump/README.md index d2baf26..f71194b 100644 --- a/compiler/circledump/README.md +++ b/compiler/circledump/README.md @@ -65,6 +65,6 @@ O T(3) ofm ### Dependency -- mio-circle04 +- mio-circle06 - safemain - FlatBuffers diff --git a/compiler/circledump/requires.cmake b/compiler/circledump/requires.cmake index 183dfe2..b3a2638 100644 --- a/compiler/circledump/requires.cmake +++ b/compiler/circledump/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-circle04") +require("mio-circle06") require("safemain") diff --git a/compiler/circledump/src/MetadataPrinter.h b/compiler/circledump/src/MetadataPrinter.h index 1dca2ca..39d92c8 100644 --- a/compiler/circledump/src/MetadataPrinter.h +++ b/compiler/circledump/src/MetadataPrinter.h @@ -29,6 +29,7 @@ class MetadataPrinter { public: virtual void print(const uint8_t * /* buffer */, std::ostream &) const = 0; + virtual ~MetadataPrinter() = default; }; class MetadataPrinterRegistry diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp index 817371d..bfcb1ec 100644 --- a/compiler/circledump/src/OpPrinter.cpp +++ b/compiler/circledump/src/OpPrinter.cpp @@ -364,6 +364,22 @@ public: } }; +class GeluPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_GeluOptions()) + { + os << " "; + os << std::boolalpha; + os << "approximate(" << params->approximate() << ") "; + os << std::noboolalpha; + os << std::endl; + } + } +}; + class IfPrinter : public OpPrinter { public: @@ -643,12 +659,14 @@ class TransposeConvPrinter : public OpPrinter public: void options(const circle::Operator *op, std::ostream &os) const override { - if (auto conv_params = op->builtin_options_as_TransposeConvOptions()) + if (auto params = op->builtin_options_as_TransposeConvOptions()) { os << " "; - os << "Padding(" << conv_params->padding() << ") "; - os << "Stride.W(" << conv_params->stride_w() << ") "; - os << "Stride.H(" << conv_params->stride_h() << ") "; + os << "Padding(" << params->padding() << ") "; + os << "Stride.W(" << params->stride_w() << ") "; + os << "Stride.H(" << params->stride_h() << ") "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; os << std::endl; } } @@ -813,6 +831,7 @@ OpPrinterRegistry::OpPrinterRegistry() // There is no Option for FLOOR_MOD _op_map[circle::BuiltinOperator_FULLY_CONNECTED] = make_unique(); _op_map[circle::BuiltinOperator_GATHER] = make_unique(); + _op_map[circle::BuiltinOperator_GELU] = make_unique(); _op_map[circle::BuiltinOperator_IF] = make_unique(); _op_map[circle::BuiltinOperator_L2_NORMALIZATION] = make_unique(); _op_map[circle::BuiltinOperator_L2_POOL_2D] = make_unique(); diff --git a/compiler/common-artifacts/CMakeLists.txt b/compiler/common-artifacts/CMakeLists.txt index 7d89d45..2b03203 100644 --- a/compiler/common-artifacts/CMakeLists.txt +++ b/compiler/common-artifacts/CMakeLists.txt @@ -17,30 +17,17 @@ if(${PYTHON_VERSION_MINOR} LESS 8) return() endif() -# Create python virtual environment with tensorflow 2.8.0 -set(VIRTUALENV_OVERLAY_TF_2_8_0 "${NNCC_OVERLAY_DIR}/venv_2_8_0") -# TensorFlow 2.10.1 for Ubuntu22.04 -if(ONE_UBUNTU_CODENAME_JAMMY) - set(VIRTUALENV_OVERLAY_TF_2_10_1 "${NNCC_OVERLAY_DIR}/venv_2_10_1") -endif(ONE_UBUNTU_CODENAME_JAMMY) +# Create python virtual environment with tensorflow 2.12.1 +set(VIRTUALENV_OVERLAY_TF_2_12_1 "${NNCC_OVERLAY_DIR}/venv_2_12_1") add_custom_command( - OUTPUT ${VIRTUALENV_OVERLAY_TF_2_8_0} - COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_2_8_0} + OUTPUT ${VIRTUALENV_OVERLAY_TF_2_12_1} + COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_2_12_1} ) # Create requirements.txt and install required pip packages set(REQUIREMENTS_FILE "requirements.txt") -set(REQUIREMENTS_OVERLAY_PATH_TF_2_8_0 "${VIRTUALENV_OVERLAY_TF_2_8_0}/${REQUIREMENTS_FILE}") - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_custom_command( - OUTPUT ${VIRTUALENV_OVERLAY_TF_2_10_1} - COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_2_10_1} - ) - set(REQUIREMENTS_FILE "requirements.txt") - set(REQUIREMENTS_OVERLAY_PATH_TF_2_10_1 "${VIRTUALENV_OVERLAY_TF_2_10_1}/${REQUIREMENTS_FILE}") -endif(ONE_UBUNTU_CODENAME_JAMMY) +set(REQUIREMENTS_OVERLAY_PATH_TF_2_12_1 "${VIRTUALENV_OVERLAY_TF_2_12_1}/${REQUIREMENTS_FILE}") set(PYTHON_OVERLAY python3) if(PYTHON_EXECUTABLE MATCHES python3.8) @@ -53,47 +40,41 @@ if(DEFINED ENV{ONE_PIP_OPTION_TRUST_HOST}) set(PIP_OPTION_TRUSTED_HOST --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --trusted-host pypi.org) endif() -# NOTE refer https://github.com/protocolbuffers/protobuf/issues/10051 -# TODO remove protobuf==3.20.1 when issue is resolved -add_custom_command( - OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.8.0" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==1.12" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${CMAKE_COMMAND} -E echo "protobuf==3.20.1" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${CMAKE_COMMAND} -E echo "pydot==1.4.2" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} - COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 - ${PIP_OPTION_TRUSTED_HOST} install --upgrade pip setuptools - COMMAND ${VIRTUALENV_OVERLAY_TF_2_8_0}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 - ${PIP_OPTION_TRUSTED_HOST} install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} --upgrade - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_8_0} -) - -add_custom_target(common_artifacts_python_deps ALL - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_8_0} - ${REQUIREMENTS_OVERLAY_PATH_TF_2_8_0} -) - -if(ONE_UBUNTU_CODENAME_JAMMY) +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + # NOTE `tensorflow-cpu` package is not available for aarch64, so we use `tensorflow` package. add_custom_command( - OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.10.1" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==23.1.21" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${CMAKE_COMMAND} -E echo "protobuf==3.19.6" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${CMAKE_COMMAND} -E echo "pydot==1.4.2" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} - COMMAND ${VIRTUALENV_OVERLAY_TF_2_10_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "tensorflow==2.12.1" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==23.5.26" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "protobuf==4.23.3" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "pydot==1.4.2" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${VIRTUALENV_OVERLAY_TF_2_12_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 ${PIP_OPTION_TRUSTED_HOST} install --upgrade pip setuptools - COMMAND ${VIRTUALENV_OVERLAY_TF_2_10_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 - ${PIP_OPTION_TRUSTED_HOST} install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} --upgrade - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_10_1} + COMMAND ${VIRTUALENV_OVERLAY_TF_2_12_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + ${PIP_OPTION_TRUSTED_HOST} install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} --upgrade + DEPENDS ${VIRTUALENV_OVERLAY_TF_2_12_1} ) - - add_custom_target(common_artifacts_python_u22_deps ALL - DEPENDS ${VIRTUALENV_OVERLAY_TF_2_10_1} - ${REQUIREMENTS_OVERLAY_PATH_TF_2_10_1} +else(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + add_custom_command( + OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.12.1" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==23.5.26" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "protobuf==4.23.3" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${CMAKE_COMMAND} -E echo "pydot==1.4.2" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} + COMMAND ${VIRTUALENV_OVERLAY_TF_2_12_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + ${PIP_OPTION_TRUSTED_HOST} install --upgrade pip setuptools + COMMAND ${VIRTUALENV_OVERLAY_TF_2_12_1}/bin/${PYTHON_OVERLAY} -m pip --default-timeout=1000 + ${PIP_OPTION_TRUSTED_HOST} install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} --upgrade + DEPENDS ${VIRTUALENV_OVERLAY_TF_2_12_1} ) -endif(ONE_UBUNTU_CODENAME_JAMMY) +endif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + +add_custom_target(common_artifacts_python_deps ALL + DEPENDS ${VIRTUALENV_OVERLAY_TF_2_12_1} + ${REQUIREMENTS_OVERLAY_PATH_TF_2_12_1} +) #[[ Generate common resources ]] # TODO add pbtxt diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst index f238e79..7505522 100644 --- a/compiler/common-artifacts/exclude.lst +++ b/compiler/common-artifacts/exclude.lst @@ -70,6 +70,8 @@ tcgenerate(Net_Dangle_001) tcgenerate(Net_Densify_Add_000) # luci-interpreter doesn't support Densify yet tcgenerate(Net_Densify_Dequantize_Add_000) # luci-interpreter doesn't support Densify/Dequantize yet tcgenerate(Net_Gather_SparseToDense_AddV2_000) # luci-interpreter doesn't support custom operator +tcgenerate(Net_Gelu_000) # luci-interpreter doesn't support custom operator +tcgenerate(Net_Gelu_001) # luci-interpreter doesn't support custom operator tcgenerate(Net_ZeroDim_001) # luci-interpreter doesn't support zero dim tcgenerate(OneHot_000) tcgenerate(OneHot_001) @@ -78,6 +80,16 @@ tcgenerate(OneHot_003) tcgenerate(Pack_000) tcgenerate(Pack_U8_000) tcgenerate(PadV2_000) +tcgenerate(Quant_Add_I8_000) # INT8 is not supported +tcgenerate(Quant_AveragePool2D_I8_000) # INT8 is not supported +tcgenerate(Quant_Conv_I8_000) # INT8 is not supported +tcgenerate(Quant_DepthwiseConv2D_I8_000) # INT8 is not supported +tcgenerate(Quant_MaxPool2D_I8_000) # INT8 is not supported +tcgenerate(Quant_Mean_I8_000) # INT8 is not supported +tcgenerate(Quant_Mul_I8_000) # INT8 is not supported +tcgenerate(Quant_PRelu_I8_000) # INT8 is not supported +tcgenerate(Quant_ReLU_I8_000) # INT8 is not supported +tcgenerate(Quant_TransposeConv_I8_000) # INT8 is not supported tcgenerate(Quantize_000) # runtime and luci-interpreter doesn't support Quantize op yet tcgenerate(Range_000) tcgenerate(Rank_000) diff --git a/compiler/dalgona-test/CMakeLists.txt b/compiler/dalgona-test/CMakeLists.txt index a233831..c8b9c25 100644 --- a/compiler/dalgona-test/CMakeLists.txt +++ b/compiler/dalgona-test/CMakeLists.txt @@ -53,17 +53,6 @@ add_test( COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/TestSingleOp.sh" "${TEST_CONFIG}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" ${DALGONA_SINGLE_OP_TEST} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test( - NAME dalgona_single_op_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/TestSingleOp.sh" - "${TEST_CONFIG}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - ${DALGONA_SINGLE_OP_TEST} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/dalgona/src/Dalgona.cpp b/compiler/dalgona/src/Dalgona.cpp index 1d060b4..1a35b6d 100644 --- a/compiler/dalgona/src/Dalgona.cpp +++ b/compiler/dalgona/src/Dalgona.cpp @@ -163,13 +163,14 @@ void Dalgona::runAnalysisWithH5Input(const std::string &input_data_path, if (is_raw_data) { // Skip type/shape check for raw data - importer.readTensor(record_idx, input_idx, input_data.data()); + importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); } else { DataType dtype; Shape shape; - importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data()); + importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), + input_data.size()); // Check the type and the shape of the input data is valid verifyTypeShape(input_node, dtype, shape); diff --git a/compiler/dalgona/src/PostOperatorHook.h b/compiler/dalgona/src/PostOperatorHook.h index daf32f6..00c5d46 100644 --- a/compiler/dalgona/src/PostOperatorHook.h +++ b/compiler/dalgona/src/PostOperatorHook.h @@ -42,6 +42,7 @@ class PostOperatorHook final : public luci::CircleNodeVisitor // 2. inputs: input data (type: std::vector of numpy array) // 3. output: output data (type: numpy array) #define POST_OPERATOR_HOOK_PROLOGUE(OP_NAME) \ + assert(not multi_out_node(node)); \ if (!py::hasattr(_analysis, #OP_NAME "Post")) \ { \ visit(loco::must_cast(node)); \ @@ -53,6 +54,7 @@ class PostOperatorHook final : public luci::CircleNodeVisitor // Multi-output version of POST_OPERATOR_HOOK_PROLOGUE #define POST_OPERATOR_HOOK_PROLOGUE_MULTI_OUTS(OP_NAME) \ + assert(multi_out_node(node)); \ if (!py::hasattr(_analysis, #OP_NAME "Post")) \ { \ visit(loco::must_cast(node)); \ @@ -81,7 +83,6 @@ public: py::object hook = _analysis.attr("DefaultOpPost"); auto inputs = inputsPyArray(node, _interpreter); - auto output = outputPyArray(node, _interpreter); py::list input_list; for (uint32_t i = 0; i < inputs.size(); i++) @@ -89,11 +90,26 @@ public: input_list.append(inputs[i]); } + py::list output_list; + if (multi_out_node(node)) + { + auto outputs = outputsPyArray(node, _interpreter); + for (uint32_t i = 0; i < outputs.size(); i++) + { + output_list.append(outputs[i]); + } + } + else + { + auto output = outputPyArray(node, _interpreter); + output_list.append(output); + } + pySafeCall(hook, node->name(), // name toString(node->opcode()), // opcode input_list, // list of inputs - output // output + output_list // list of outputs ); } diff --git a/compiler/dalgona/src/Utils.cpp b/compiler/dalgona/src/Utils.cpp index f65d9c2..a5b0bb5 100644 --- a/compiler/dalgona/src/Utils.cpp +++ b/compiler/dalgona/src/Utils.cpp @@ -170,6 +170,28 @@ py::dict outputPyArray(const luci::CircleNode *node, luci_interpreter::Interpret return py_output; } +bool multi_out_node(const luci::CircleNode *node) +{ + switch (node->opcode()) + { + // TODO Update this list when new Op is added + // Tip: grep "public GraphBuilderMultiOutput" in luci/import + case luci::CircleOpcode::BIDIRECTIONAL_SEQUENCE_LSTM: + case luci::CircleOpcode::CUSTOM: + case luci::CircleOpcode::IF: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V4: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V5: + case luci::CircleOpcode::SPLIT: + case luci::CircleOpcode::SPLIT_V: + case luci::CircleOpcode::TOPK_V2: + case luci::CircleOpcode::UNIQUE: + case luci::CircleOpcode::UNPACK: + return true; + default: + return false; + } +} + } // namespace dalgona #undef THROW_UNLESS diff --git a/compiler/dalgona/src/Utils.h b/compiler/dalgona/src/Utils.h index 9ca920f..7bda307 100644 --- a/compiler/dalgona/src/Utils.h +++ b/compiler/dalgona/src/Utils.h @@ -51,6 +51,8 @@ std::vector outputsPyArray(const luci::CircleNode *node, py::object none(); +bool multi_out_node(const luci::CircleNode *node); + } // namespace dalgona #endif // __DALGONA_UTILS_H__ diff --git a/compiler/dio-hdf5/include/dio_hdf5/HDF5Importer.h b/compiler/dio-hdf5/include/dio_hdf5/HDF5Importer.h index aafcfbb..add4411 100644 --- a/compiler/dio-hdf5/include/dio_hdf5/HDF5Importer.h +++ b/compiler/dio-hdf5/include/dio_hdf5/HDF5Importer.h @@ -58,12 +58,13 @@ public: * @param dtype : pointer to write the tensor's data type * @param shape : pointer to write the tensor's shape * @param buffer : pointer to write the tensor's data + * @param buffer_bytes : byte size of the buffer */ void readTensor(int32_t data_idx, int32_t input_idx, loco::DataType *dtype, - std::vector *shape, void *buffer); + std::vector *shape, void *buffer, size_t buffer_bytes); // Read a raw tensor (no type/shape is specified) - void readTensor(int32_t data_idx, int32_t input_idx, void *buffer); + void readTensor(int32_t data_idx, int32_t input_idx, void *buffer, size_t buffer_bytes); bool isRawData() { return _group.attrExists("rawData"); } diff --git a/compiler/dio-hdf5/src/HDF5Importer.cpp b/compiler/dio-hdf5/src/HDF5Importer.cpp index 9ae556b..9208990 100644 --- a/compiler/dio-hdf5/src/HDF5Importer.cpp +++ b/compiler/dio-hdf5/src/HDF5Importer.cpp @@ -128,16 +128,20 @@ int32_t HDF5Importer::numInputs(int32_t record_idx) return records.getNumObjs(); } -void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, void *buffer) +void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, void *buffer, + size_t buffer_bytes) { auto record = _group.openGroup(std::to_string(record_idx)); auto tensor = record.openDataSet(std::to_string(input_idx)); + if (tensor.getInMemDataSize() != buffer_bytes) + throw std::runtime_error("Buffer size does not match with the size of tensor data"); + readTensorData(tensor, static_cast(buffer)); } void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, DataType *dtype, Shape *shape, - void *buffer) + void *buffer, size_t buffer_bytes) { auto record = _group.openGroup(std::to_string(record_idx)); auto tensor = record.openDataSet(std::to_string(input_idx)); @@ -148,6 +152,9 @@ void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, DataType *d auto tensor_shape = tensor.getSpace(); *shape = toInternalShape(tensor_shape); + if (tensor.getInMemDataSize() != buffer_bytes) + throw std::runtime_error("Buffer size does not match with the size of tensor data"); + switch (*dtype) { case DataType::FLOAT32: diff --git a/compiler/dio-hdf5/src/HDF5Importer.test.cpp b/compiler/dio-hdf5/src/HDF5Importer.test.cpp index 61a027f..1433e76 100644 --- a/compiler/dio-hdf5/src/HDF5Importer.test.cpp +++ b/compiler/dio-hdf5/src/HDF5Importer.test.cpp @@ -73,7 +73,7 @@ TEST(dio_hdf5_test, read_with_type_shape) DataType dtype; Shape shape; - h5.readTensor(0, 0, &dtype, &shape, buffer.data()); + h5.readTensor(0, 0, &dtype, &shape, buffer.data(), buffer.size() * sizeof(float)); for (uint32_t i = 0; i < 6; i++) EXPECT_EQ(i, buffer[i]); @@ -114,7 +114,8 @@ TEST(dio_hdf5_test, data_out_of_index_NEG) DataType dtype; Shape shape; // Read non-existing data (data_idx = 1) - EXPECT_ANY_THROW(h5.readTensor(1, 0, &dtype, &shape, buffer.data())); + EXPECT_ANY_THROW( + h5.readTensor(1, 0, &dtype, &shape, buffer.data(), buffer.size() * sizeof(float))); } TEST(dio_hdf5_test, input_out_of_index_NEG) @@ -130,5 +131,21 @@ TEST(dio_hdf5_test, input_out_of_index_NEG) DataType dtype; Shape shape; // Read non-existing input (input_idx = 1) - EXPECT_ANY_THROW(h5.readTensor(0, 1, &dtype, &shape, buffer.data())); + EXPECT_ANY_THROW( + h5.readTensor(0, 1, &dtype, &shape, buffer.data(), buffer.size() * sizeof(float))); +} + +TEST(dio_hdf5_test, wrong_buffer_size_NEG) +{ + createFile(); + + HDF5Importer h5(::file_name); + + h5.importGroup("value"); + + std::vector buffer(6); + + DataType dtype; + Shape shape; + EXPECT_ANY_THROW(h5.readTensor(0, 0, &dtype, &shape, buffer.data(), 1 /* wrong buffer size */)); } diff --git a/compiler/loco/CMakeLists.txt b/compiler/loco/CMakeLists.txt index 52011ec..d885805 100644 --- a/compiler/loco/CMakeLists.txt +++ b/compiler/loco/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries(loco PUBLIC nncc_coverage) # Q. HOW TO MAKE DEV PACKAGE(?) install(TARGETS loco DESTINATION lib) install(DIRECTORY include/ DESTINATION include - FILES_MATCHING PATTERN "*.h") + FILES_MATCHING PATTERN "*.h" PATTERN "*.lst") if(NOT ENABLE_TEST) return() diff --git a/compiler/luci-eval-driver/src/EvalDriver.cpp b/compiler/luci-eval-driver/src/EvalDriver.cpp index 8d844ab..fb48f67 100644 --- a/compiler/luci-eval-driver/src/EvalDriver.cpp +++ b/compiler/luci-eval-driver/src/EvalDriver.cpp @@ -93,7 +93,12 @@ int entry(int argc, char **argv) // Data for n'th input is read from ${input_prefix}n // (ex: Add.circle.input0, Add.circle.input1 ..) const auto input_nodes = loco::input_nodes(module->graph()); - assert(num_inputs == input_nodes.size()); + if (num_inputs != input_nodes.size()) + { + // NOTE using num_inputs is actually unnecessary but is kept to preserve interface. + std::cerr << "ERROR: invalid num_inputs value; should be " << input_nodes.size() << std::endl; + return EXIT_FAILURE; + } for (int32_t i = 0; i < num_inputs; i++) { const auto *input_node = loco::must_cast(input_nodes[i]); diff --git a/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h b/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h index bb9ff6d..ad33887 100644 --- a/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h +++ b/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h @@ -166,9 +166,9 @@ private: DataType _element_type; Shape _shape; AffineQuantization _quantization; - uint8_t *_data; + uint8_t *_data = nullptr; std::string _name; - bool _data_allocated; + bool _data_allocated = false; // Write of tensor is reported to registered Observers only if this tensor is observable // This is needed for tensors used in kernel implementation, but not present in original model. bool _is_observable = true; diff --git a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst index db31cbf..e4d42de 100644 --- a/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst +++ b/compiler/luci-interpreter/pal/linux/KernelsToBuild.lst @@ -17,11 +17,14 @@ REGISTER_KERNEL(ExpandDims) REGISTER_KERNEL(Fill) REGISTER_KERNEL(Floor) REGISTER_KERNEL(FloorDiv) +REGISTER_KERNEL(FloorMod) REGISTER_KERNEL(Equal) REGISTER_KERNEL(FullyConnected) REGISTER_KERNEL(Gather) +REGISTER_KERNEL(Gelu) REGISTER_KERNEL(Greater) REGISTER_KERNEL(GreaterEqual) +REGISTER_KERNEL(HardSwish) REGISTER_KERNEL(If) REGISTER_KERNEL(InstanceNorm) REGISTER_KERNEL(L2Normalize) @@ -30,6 +33,7 @@ REGISTER_KERNEL(LeakyRelu) REGISTER_KERNEL(Less) REGISTER_KERNEL(LessEqual) REGISTER_KERNEL(LocalResponseNormalization) +REGISTER_KERNEL(Log) REGISTER_KERNEL(LogicalAnd) REGISTER_KERNEL(LogicalNot) REGISTER_KERNEL(LogicalOr) @@ -59,6 +63,7 @@ REGISTER_KERNEL(ResizeBilinear) REGISTER_KERNEL(ResizeNearestNeighbor) REGISTER_KERNEL(ReverseV2) REGISTER_KERNEL(Rsqrt) +REGISTER_KERNEL(Select) REGISTER_KERNEL(Shape) REGISTER_KERNEL(Slice) REGISTER_KERNEL(Softmax) @@ -72,6 +77,7 @@ REGISTER_KERNEL(Square) REGISTER_KERNEL(SquaredDifference) REGISTER_KERNEL(Squeeze) REGISTER_KERNEL(Sub) +REGISTER_KERNEL(Sum) REGISTER_KERNEL(SVDF) REGISTER_KERNEL(Tanh) REGISTER_KERNEL(Transpose) diff --git a/onert-micro/luci-interpreter/pal/linux/PALElu.h b/compiler/luci-interpreter/pal/linux/PALGelu.h similarity index 53% rename from onert-micro/luci-interpreter/pal/linux/PALElu.h rename to compiler/luci-interpreter/pal/linux/PALGelu.h index 7746d96..e1796e7 100644 --- a/onert-micro/luci-interpreter/pal/linux/PALElu.h +++ b/compiler/luci-interpreter/pal/linux/PALGelu.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,18 +14,19 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_ELU_H -#define LUCI_INTERPRETER_PAL_ELU_H +#ifndef LUCI_INTERPRETER_PAL_GELU_H +#define LUCI_INTERPRETER_PAL_GELU_H -#include +#include namespace luci_interpreter_pal { -static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) +static inline void Gelu(bool approximate, const tflite::RuntimeShape &input_shape, + const float *input_data, const tflite::RuntimeShape &output_shape, + float *output_data) { - tflite::optimized_ops::Elu(input_shape, input_data, output_shape, output_data); + tflite::reference_ops::Gelu(input_shape, input_data, approximate, output_shape, output_data); } } // namespace luci_interpreter_pal -#endif // LUCI_INTERPRETER_PAL_ELU_H +#endif // LUCI_INTERPRETER_PAL_GELU_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALLeakyRelu.h b/compiler/luci-interpreter/pal/linux/PALHardSwish.h similarity index 63% rename from onert-micro/luci-interpreter/pal/linux/PALLeakyRelu.h rename to compiler/luci-interpreter/pal/linux/PALHardSwish.h index 506efb2..2ce7cb3 100644 --- a/onert-micro/luci-interpreter/pal/linux/PALLeakyRelu.h +++ b/compiler/luci-interpreter/pal/linux/PALHardSwish.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +14,18 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H -#define LUCI_INTERPRETER_PAL_LEAKYRELU_H +#ifndef LUCI_INTERPRETER_PAL_HARDSWISH_H +#define LUCI_INTERPRETER_PAL_HARDSWISH_H #include namespace luci_interpreter_pal { -static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, +static inline void HardSwish(const tflite::RuntimeShape &input_shape, const float *input_data, const tflite::RuntimeShape &output_shape, float *output_data) { - tflite::optimized_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); + tflite::optimized_ops::HardSwish(input_shape, input_data, output_shape, output_data); } } // namespace luci_interpreter_pal -#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H +#endif // LUCI_INTERPRETER_PAL_HARDSWISH_H diff --git a/compiler/luci-interpreter/pal/linux/pal.cmake b/compiler/luci-interpreter/pal/linux/pal.cmake index 185700c..28f6352 100644 --- a/compiler/luci-interpreter/pal/linux/pal.cmake +++ b/compiler/luci-interpreter/pal/linux/pal.cmake @@ -1,8 +1,34 @@ +# set target platform to run +if(NOT TARGET_ARCH OR "${TARGET_ARCH}" STREQUAL "") + string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} TARGET_ARCH) +else() + string(TOLOWER ${TARGET_ARCH} TARGET_ARCH) +endif() + +# If TARGET_ARCH is arm64 change ARCH name to aarch64 +if("${TARGET_ARCH}" STREQUAL "arm64") + set(TARGET_ARCH "aarch64") +endif() + +if("${TARGET_ARCH}" STREQUAL "armv8-m") + set(TARGET_ARCH_BASE "arm") +elseif("${TARGET_ARCH}" STREQUAL "armv7-r") + set(TARGET_ARCH_BASE "arm") +elseif("${TARGET_ARCH}" STREQUAL "armv7em") + set(TARGET_ARCH_BASE "arm") +elseif("${TARGET_ARCH}" STREQUAL "armv7l") + set(TARGET_ARCH_BASE "arm") +elseif("${TARGET_ARCH}" STREQUAL "armv7hl") + set(TARGET_ARCH_BASE "arm") +elseif("${TARGET_ARCH}" STREQUAL "aarch64") + set(TARGET_ARCH_BASE "aarch64") +endif() + macro(initialize_pal) - nnas_find_package(TensorFlowSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowEigenSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowRuySource EXACT 2.6.0 QUIET) + nnas_find_package(TensorFlowSource EXACT 2.8.0 QUIET) + nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.8.0 QUIET) + nnas_find_package(TensorFlowEigenSource EXACT 2.8.0 QUIET) + nnas_find_package(TensorFlowRuySource EXACT 2.8.0 QUIET) if (NOT TensorFlowSource_FOUND) message(STATUS "Skipping luci-interpreter: TensorFlow not found") @@ -44,7 +70,7 @@ macro(add_pal_to_target TGT) ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc) - if(BUILD_ARM32_NEON) + if(TARGET_ARCH_BASE STREQUAL "arm") # NOTE may need to revise this list for version upgrade set(PAL_SOURCES ${PAL_SOURCES} ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc @@ -67,7 +93,32 @@ macro(add_pal_to_target TGT) ${TensorFlowRuySource_DIR}/ruy/wait.cc ${TensorFlowRuySource_DIR}/ruy/kernel_arm32.cc ) - endif(BUILD_ARM32_NEON) + endif(TARGET_ARCH_BASE STREQUAL "arm") + + if(TARGET_ARCH_BASE STREQUAL "aarch64") + # NOTE may need to revise this list for version upgrade + set(PAL_SOURCES ${PAL_SOURCES} + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc + ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/cpu_check.cc + ${TensorFlowRuySource_DIR}/ruy/allocator.cc + ${TensorFlowRuySource_DIR}/ruy/block_map.cc + ${TensorFlowRuySource_DIR}/ruy/blocking_counter.cc + ${TensorFlowRuySource_DIR}/ruy/context_get_ctx.cc + ${TensorFlowRuySource_DIR}/ruy/cpuinfo.cc + ${TensorFlowRuySource_DIR}/ruy/ctx.cc + ${TensorFlowRuySource_DIR}/ruy/denormal.cc + ${TensorFlowRuySource_DIR}/ruy/frontend.cc + ${TensorFlowRuySource_DIR}/ruy/pack_arm.cc + ${TensorFlowRuySource_DIR}/ruy/prepacked_cache.cc + ${TensorFlowRuySource_DIR}/ruy/prepare_packed_matrices.cc + ${TensorFlowRuySource_DIR}/ruy/system_aligned_alloc.cc + ${TensorFlowRuySource_DIR}/ruy/thread_pool.cc + ${TensorFlowRuySource_DIR}/ruy/trmul.cc + ${TensorFlowRuySource_DIR}/ruy/tune.cc + ${TensorFlowRuySource_DIR}/ruy/wait.cc + ${TensorFlowRuySource_DIR}/ruy/kernel_arm64.cc + ) + endif(TARGET_ARCH_BASE STREQUAL "aarch64") add_library(luci_interpreter_linux_pal STATIC ${PAL_SOURCES}) set_target_properties(luci_interpreter_linux_pal PROPERTIES POSITION_INDEPENDENT_CODE ON) diff --git a/compiler/luci-interpreter/src/core/KernelParams.h b/compiler/luci-interpreter/src/core/KernelParams.h index 45f3bfe..4ddbcef 100644 --- a/compiler/luci-interpreter/src/core/KernelParams.h +++ b/compiler/luci-interpreter/src/core/KernelParams.h @@ -98,6 +98,11 @@ struct GatherParams int32_t batch_dims; }; +struct GeluParams +{ + bool approximate; +}; + struct InstanceNormParams { float epsilon; @@ -216,6 +221,7 @@ struct TransposeConvParams Padding padding; int32_t stride_height; int32_t stride_width; + Activation activation; }; struct UnidirectionalSequenceLSTMParams diff --git a/compiler/luci-interpreter/src/kernels/FloorMod.cpp b/compiler/luci-interpreter/src/kernels/FloorMod.cpp new file mode 100644 index 0000000..a64fcad --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/FloorMod.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 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/FloorMod.h" +#include "kernels/Utils.h" + +#include +#include + +namespace +{ + +template T FloorDivFunc(T input1, T input2) +{ + struct FloatMod + { + float operator()(const float lhs, const float rhs) const { return std::fmod(lhs, rhs); } + }; + using ModFunc = + typename std::conditional::value, std::modulus, FloatMod>::type; + ModFunc mod_func; + T trunc_mod = mod_func(input1, input2); + return (trunc_mod != 0) && ((input2 < 0) != (trunc_mod < 0)) ? (trunc_mod + input2) : trunc_mod; +} + +} // namespace + +namespace luci_interpreter +{ + +namespace kernels +{ + +FloorMod::FloorMod(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + +void FloorMod::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 FloorMod::execute() const +{ + switch (x()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + case DataType::S8: + evalInteger(); + break; + case DataType::S16: + evalInteger(); + break; + case DataType::S32: + evalInteger(); + break; + case DataType::S64: + evalInteger(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void FloorMod::evalFloat() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + + if (x()->shape() != y()->shape()) + { + tflite::reference_ops::BroadcastBinaryFunction4DSlow( + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } + else + { + tflite::reference_ops::BinaryFunction( + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } +} + +template void FloorMod::evalInteger() const +{ + const auto x_data = getTensorData(x()); + const auto y_data = getTensorData(y()); + + // Check the denominator + const auto y_data_type = y()->element_type(); + if (y_data_type == DataType::S8 || y_data_type == DataType::S16 || y_data_type == DataType::S32 || + y_data_type == DataType::S64) + { + for (int i = 0; i < getTensorShape(y()).FlatSize(); ++i) + { + LUCI_INTERPRETER_CHECK(y_data[i] != 0); + } + } + + if (x()->shape() != y()->shape()) + { + tflite::reference_ops::BroadcastBinaryFunction4DSlow( + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } + else + { + tflite::reference_ops::BinaryFunction(getTensorShape(x()), x_data, getTensorShape(y()), + y_data, getTensorShape(output()), + getTensorData(output()), FloorDivFunc); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Less.h b/compiler/luci-interpreter/src/kernels/FloorMod.h similarity index 68% rename from onert-micro/luci-interpreter/src/kernels/Less.h rename to compiler/luci-interpreter/src/kernels/FloorMod.h index 364e5a9..f2d9b2a 100644 --- a/onert-micro/luci-interpreter/src/kernels/Less.h +++ b/compiler/luci-interpreter/src/kernels/FloorMod.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,8 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_LESS_H -#define LUCI_INTERPRETER_KERNELS_LESS_H +#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_MOD_H +#define LUCI_INTERPRETER_KERNELS_FLOOR_MOD_H #include "core/Kernel.h" @@ -25,10 +24,10 @@ namespace luci_interpreter namespace kernels { -class Less : public Kernel +class FloorMod : public Kernel { public: - Less(const Tensor *x, const Tensor *y, Tensor *output); + FloorMod(const Tensor *x, const Tensor *y, Tensor *output); const Tensor *x() const { return _inputs[0]; } const Tensor *y() const { return _inputs[1]; } @@ -40,16 +39,9 @@ public: private: void evalFloat() const; template void evalInteger() const; - void evalQuantized() const; - -private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; }; } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_LESS_H +#endif // LUCI_INTERPRETER_KERNELS_FLOOR_MOD_H diff --git a/compiler/luci-interpreter/src/kernels/FloorMod.test.cpp b/compiler/luci-interpreter/src/kernels/FloorMod.test.cpp new file mode 100644 index 0000000..123a91e --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/FloorMod.test.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2023 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/FloorMod.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class FloorModTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(FloorModTest, Simple) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10, 9, 11, 3}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2, 2, 3, 4}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0, 1, 2, 3}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, NegativeValue) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10, -9, -11, 7}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2, 2, -3, -4}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0, 1, -2, -1}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, BroadcastFloorMod) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{ + 10, + -9, + -11, + 7, + }; + + Shape input2_shape{1}; + std::vector input2_data{-3}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{-2, 0, -2, -2}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, Int64WithBroadcast) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10, -9, -11, (1LL << 34) + 9}; + + Shape input2_shape{1}; + std::vector input2_data{-(1LL << 33)}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{-8589934582, -9, -11, -8589934583}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, FloatSimple) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10.0, 9.0, 11.0, 3.0}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2.0, 2.0, 3.0, 4.0}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0.0, 1.0, 2.0, 3.0}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, FloatNegativeValue) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10.0, -9.0, -11.0, 7.0}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2.0, 2.0, -3.0, -4.0}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0.0, 1.0, -2.0, -1.0}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, FloatBroadcast) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{ + 10.0, + -9.0, + -11.0, + 7.0, + }; + + Shape input2_shape{1}; + std::vector input2_data{-3.0}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{-2.0, 0.0, -2.0, -2.0}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, SimpleInt16) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10, 9, 11, 3}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2, 2, 3, 4}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0, 1, 2, 3}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, NegativeValueInt16) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{110, -9, -11, 7}; + + Shape input2_shape = input1_shape; + std::vector input2_data{2, 2, -3, -4}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{0, 1, -2, -1}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, BroadcastFloorModInt16) +{ + Shape input1_shape{1, 2, 2, 1}; + std::vector input1_data{10, -9, -11, 7}; + + Shape input2_shape{1}; + std::vector input2_data{-3}; + + std::vector ref_output_shape{1, 2, 2, 1}; + std::vector ref_output_data{-2, 0, -2, -2}; + + Tensor input1_tensor = + makeInputTensor(input1_shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor(input2_shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(FloorModTest, DivByZero_NEG) +{ + Shape shape{3}; + std::vector input1_data{1, 0, -1}; + std::vector input2_data{0, 0, 0}; + + Tensor input1_tensor = makeInputTensor(shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(FloorModTest, Int64DivByZero_NEG) +{ + Shape shape{3}; + std::vector input1_data{1, 0, -1}; + std::vector input2_data{0, 0, 0}; + + Tensor input1_tensor = makeInputTensor(shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(FloorModTest, Int16DivByZero_NEG) +{ + Shape shape{3}; + std::vector input1_data{1, 0, -1}; + std::vector input2_data{0, 0, 0}; + + Tensor input1_tensor = makeInputTensor(shape, input1_data, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor(shape, input2_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + + EXPECT_ANY_THROW(kernel.execute()); +} + +TEST_F(FloorModTest, Input_Output_Type_Mismatch_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S8); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, Input_Type_Mismatch_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, Float_Broadcast_NEG) +{ + Tensor input1_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); + Tensor input2_tensor = + makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, Int64_Broadcast_NEG) +{ + Tensor input1_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, Int32_Broadcast_NEG) +{ + Tensor input1_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S32); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, Int16_Broadcast_NEG) +{ + Tensor input1_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S16); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + ASSERT_ANY_THROW(kernel.configure()); +} + +TEST_F(FloorModTest, UnsupportedType_NEG) +{ + Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor input2_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + FloorMod kernel(&input1_tensor, &input2_tensor, &output_tensor); + kernel.configure(); + + _memory_manager->allocate_memory(output_tensor); + ASSERT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Gelu.cpp b/compiler/luci-interpreter/src/kernels/Gelu.cpp new file mode 100644 index 0000000..44e018e --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Gelu.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/Gelu.h" + +#include "kernels/Utils.h" + +#include "PALGelu.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Gelu::Gelu(const Tensor *input, Tensor *output, const GeluParams ¶ms) + : KernelWithParams({input}, {output}, params) +{ +} + +void Gelu::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + output()->resize(input()->shape()); +} + +void Gelu::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Gelu::evalFloat() const +{ + luci_interpreter_pal::Gelu(params().approximate, getTensorShape(input()), + getTensorData(input()), getTensorShape(output()), + getTensorData(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Exp.h b/compiler/luci-interpreter/src/kernels/Gelu.h similarity index 73% rename from onert-micro/luci-interpreter/src/kernels/Exp.h rename to compiler/luci-interpreter/src/kernels/Gelu.h index 51ba2ca..c7c8bd9 100644 --- a/onert-micro/luci-interpreter/src/kernels/Exp.h +++ b/compiler/luci-interpreter/src/kernels/Gelu.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,8 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_EXP_H -#define LUCI_INTERPRETER_KERNELS_EXP_H +#ifndef LUCI_INTERPRETER_KERNELS_GELU_H +#define LUCI_INTERPRETER_KERNELS_GELU_H #include "core/Kernel.h" #include "core/KernelParams.h" @@ -26,10 +25,10 @@ namespace luci_interpreter namespace kernels { -class Exp : public Kernel +class Gelu : public KernelWithParams { public: - Exp(const Tensor *input, Tensor *output); + Gelu(const Tensor *input, Tensor *output, const GeluParams ¶ms); const Tensor *input() const { return _inputs[0]; } Tensor *output() const { return _outputs[0]; } @@ -39,9 +38,12 @@ public: private: void evalFloat() const; + +private: + bool _approximate = false; }; } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_EXP_H +#endif // LUCI_INTERPRETER_KERNELS_GELU_H diff --git a/compiler/luci-interpreter/src/kernels/Gelu.test.cpp b/compiler/luci-interpreter/src/kernels/Gelu.test.cpp new file mode 100644 index 0000000..6442809 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Gelu.test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 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/Gelu.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data, + bool approximate) +{ + std::unique_ptr memory_manager = std::make_unique(); + constexpr DataType element_type = getElementType(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(element_type); + + GeluParams params{}; + params.approximate = approximate; + + Gelu kernel(&input_tensor, &output_tensor, params); + + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +class GeluTest : public ::testing::Test +{ +}; + +TEST_F(GeluTest, Simple) +{ + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, 0.841345f, 2.99595f, // Row 1 + 0.841345f, -0.158655f, -0.0455003f, // Row 2 + }, + /*approximate=*/false); + + SUCCEED(); +} + +TEST_F(GeluTest, Approximate) +{ + Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, 0.841192f, 2.99636f, // Row 1 + 0.841192f, -0.158808f, -0.0454023f, // Row 2 + }, + /*approximate=*/true); + + SUCCEED(); +} + +TEST_F(GeluTest, DifferentInOutType_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = makeInputTensor({2, 3}, + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + GeluParams params{}; + params.approximate = false; + + Gelu 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/HardSwish.cpp b/compiler/luci-interpreter/src/kernels/HardSwish.cpp new file mode 100644 index 0000000..b100845 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/HardSwish.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/HardSwish.h" +#include "kernels/Utils.h" + +#include "PALHardSwish.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +HardSwish::HardSwish(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void HardSwish::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + output()->resize(input()->shape()); +} + +void HardSwish::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + luci_interpreter_pal::HardSwish(getTensorShape(input()), getTensorData(input()), + getTensorShape(output()), getTensorData(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Elu.h b/compiler/luci-interpreter/src/kernels/HardSwish.h similarity index 74% rename from onert-micro/luci-interpreter/src/kernels/Elu.h rename to compiler/luci-interpreter/src/kernels/HardSwish.h index 67a922b..bb9e9b6 100644 --- a/onert-micro/luci-interpreter/src/kernels/Elu.h +++ b/compiler/luci-interpreter/src/kernels/HardSwish.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,8 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_ELU_H -#define LUCI_INTERPRETER_KERNELS_ELU_H +#ifndef LUCI_INTERPRETER_KERNELS_HARDSWISH_H +#define LUCI_INTERPRETER_KERNELS_HARDSWISH_H #include "core/Kernel.h" #include "core/KernelParams.h" @@ -26,10 +25,10 @@ namespace luci_interpreter namespace kernels { -class Elu : public Kernel +class HardSwish : public Kernel { public: - Elu(const Tensor *input, Tensor *output); + HardSwish(const Tensor *input, Tensor *output); const Tensor *input() const { return _inputs[0]; } Tensor *output() const { return _outputs[0]; } @@ -41,4 +40,4 @@ public: } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_ELU_H +#endif // LUCI_INTERPRETER_KERNELS_HARDSWISH_H diff --git a/compiler/luci-interpreter/src/kernels/HardSwish.test.cpp b/compiler/luci-interpreter/src/kernels/HardSwish.test.cpp new file mode 100644 index 0000000..c055fee --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/HardSwish.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/HardSwish.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +void Check(std::initializer_list input_shape, std::initializer_list output_shape, + std::initializer_list input_data, std::initializer_list output_data) +{ + std::unique_ptr memory_manager = std::make_unique(); + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + HardSwish kernel(&input_tensor, &output_tensor); + kernel.configure(); + memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + (void)output_shape; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); +} + +TEST(HardSwishTest, SimpleHardSwish) +{ + Check( + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // + }, + /*output_data=*/ + { + 0, -0, 1.66667, -0, // + 3, -0.333333, 10, -0.0483333, // + }); +} + +TEST(HardSwishTest, InOutTypeMismatch_NEG) +{ + std::unique_ptr memory_manager = std::make_unique(); + Shape input_shape{1, 2, 4, 1}; + std::vector input_data{ + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // + }; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + HardSwish kernel(&input_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Log.cpp b/compiler/luci-interpreter/src/kernels/Log.cpp new file mode 100644 index 0000000..fa5f90e --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Log.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/Log.h" +#include "kernels/Utils.h" + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Log::Log(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Log::configure() { output()->resize(input()->shape()); } + +void Log::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Log::evalFloat() const +{ + const auto input_data = getTensorData(input()); + const auto input_shape = input()->shape(); + auto output_data = getTensorData(output()); + for (int64_t i = 0; i < input_shape.num_elements(); ++i) + { + output_data[i] = std::log(input_data[i]); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Relu6.h b/compiler/luci-interpreter/src/kernels/Log.h similarity index 73% rename from onert-micro/luci-interpreter/src/kernels/Relu6.h rename to compiler/luci-interpreter/src/kernels/Log.h index 61aba67..49b2937 100644 --- a/onert-micro/luci-interpreter/src/kernels/Relu6.h +++ b/compiler/luci-interpreter/src/kernels/Log.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +14,8 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_RELU6_H -#define LUCI_INTERPRETER_KERNELS_RELU6_H +#ifndef LUCI_INTERPRETER_KERNELS_LOG_H +#define LUCI_INTERPRETER_KERNELS_LOG_H #include "core/Kernel.h" @@ -25,10 +24,10 @@ namespace luci_interpreter namespace kernels { -class Relu6 : public Kernel +class Log : public Kernel { public: - Relu6(const Tensor *input, Tensor *output); + Log(const Tensor *input, Tensor *output); const Tensor *input() const { return _inputs[0]; } Tensor *output() const { return _outputs[0]; } @@ -38,7 +37,6 @@ public: private: void evalFloat() const; - void evalQuantized() const; private: int32_t _output_multiplier{0}; @@ -48,4 +46,4 @@ private: } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_RELU6_H +#endif // LUCI_INTERPRETER_KERNELS_LOG_H diff --git a/compiler/luci-interpreter/src/kernels/Log.test.cpp b/compiler/luci-interpreter/src/kernels/Log.test.cpp new file mode 100644 index 0000000..3e360e0 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Log.test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 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/Log.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class LogTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(LogTest, FloatSimple) +{ + std::vector input_data{1, 3.1415926, 1, 1}; + + std::vector ref_output_data{0, 1.14473, 0, 0}; + + Tensor input_tensor = + makeInputTensor({1, 1, 4, 1}, input_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Log kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 4, 1})); +} + +TEST_F(LogTest, Invalid_Input_Type_NEG) +{ + Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::S64); + + Log kernel(&input_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + EXPECT_ANY_THROW(kernel.execute()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp index 61d3300..d3234e4 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp @@ -17,6 +17,8 @@ #include "kernels/Reshape.h" +#include "kernels/Utils.h" + #include #include @@ -28,12 +30,26 @@ namespace kernels static Shape extractShapeFromTensor(const Tensor *tensor) { - assert(tensor->element_type() == DataType::S32); Shape shape(tensor->shape().num_elements()); - const auto *shape_data = tensor->data(); - for (int i = 0; i < tensor->shape().num_elements(); ++i) + if (tensor->element_type() == DataType::S32) + { + const auto *shape_data = tensor->data(); + for (int i = 0; i < tensor->shape().num_elements(); ++i) + { + shape.dim(i) = shape_data[i]; + } + } + else if (tensor->element_type() == DataType::S64) + { + const auto *shape_data = tensor->data(); + for (int i = 0; i < tensor->shape().num_elements(); ++i) + { + shape.dim(i) = static_cast(shape_data[i]); + } + } + else { - shape.dim(i) = shape_data[i]; + LUCI_INTERPRETER_CHECK(false); } return shape; } diff --git a/compiler/luci-interpreter/src/kernels/Reshape.test.cpp b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp index c2ff3ea..7c0522e 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp @@ -77,6 +77,42 @@ TEST_F(ReshapeTest, UnknownDimension) EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); } +TEST_F(ReshapeTest, SupportS64) +{ + Shape input_shape{2, 1, 2, 3}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape shape_shape{3}; + std::vector shape_data{2, -1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor shape_tensor = + makeInputTensor(shape_shape, shape_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); +} + +TEST_F(ReshapeTest, SupportS16_NEG) +{ + Shape input_shape{2, 1, 2, 3}; + std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Shape shape_shape{3}; + std::vector shape_data{2, -1, 2}; + Tensor input_tensor = + makeInputTensor(input_shape, input_data, _memory_manager.get()); + Tensor shape_tensor = + makeInputTensor(shape_shape, shape_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + } // namespace } // namespace kernels } // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Select.cpp b/compiler/luci-interpreter/src/kernels/Select.cpp new file mode 100644 index 0000000..b4ab5f6 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Select.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 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/Select.h" +#include "kernels/Utils.h" + +#include +// TODO use select.h when version up +// #include + +#include + +namespace luci_interpreter +{ + +namespace kernels +{ + +Select::Select(const Tensor *condition, const Tensor *t, const Tensor *e, Tensor *output) + : Kernel({condition, t, e}, {output}) +{ + // NOTE _requires_broadcast is for SelectV2 + _requires_broadcast = false; + _has_low_rank_input_condition = false; +} + +void Select::configure() +{ + LUCI_INTERPRETER_CHECK(condition()->element_type() == DataType::BOOL); + LUCI_INTERPRETER_CHECK(t()->element_type() == e()->element_type()); + LUCI_INTERPRETER_CHECK(t()->element_type() == output()->element_type()); + + auto cond_shape = condition()->shape(); + auto cond_num_dims = cond_shape.num_dims(); + auto t_shape = t()->shape(); + + bool is_input_condition_scalar = cond_num_dims == 0; + bool has_rank_one_input_condition = cond_num_dims == 1 && cond_shape.dim(0) == t_shape.dim(0); + + _has_low_rank_input_condition = is_input_condition_scalar || has_rank_one_input_condition; + + output()->resize(calculateShapeForBroadcast(t()->shape(), e()->shape())); +} + +void Select::execute() const +{ + switch (t()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Select: unsupported type."); + } +} + +void Select::evalFloat() const +{ + const auto condition_shape = getTensorShape(condition()); + const auto condition_data = getTensorData(condition()); + const auto t_shape = getTensorShape(t()); + const auto t_data = getTensorData(t()); + const auto e_shape = getTensorShape(e()); + const auto e_data = getTensorData(e()); + const auto output_shape = getTensorShape(output()); + auto output_data = getTensorData(output()); + + if (_has_low_rank_input_condition) + { + tflite::reference_ops::RankOneSelect(condition_shape, condition_data, t_shape, t_data, e_shape, + e_data, output_shape, output_data); + } + else if (_requires_broadcast) + { + // TODO support broadcast kernel when upgrade to TF2.10.x or above + assert(false); + } + else + { + tflite::reference_ops::Select(condition_shape, condition_data, t_shape, t_data, e_shape, e_data, + output_shape, output_data); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Greater.h b/compiler/luci-interpreter/src/kernels/Select.h similarity index 54% rename from onert-micro/luci-interpreter/src/kernels/Greater.h rename to compiler/luci-interpreter/src/kernels/Select.h index 8e4369f..d67b4f5 100644 --- a/onert-micro/luci-interpreter/src/kernels/Greater.h +++ b/compiler/luci-interpreter/src/kernels/Select.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 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. @@ -15,8 +15,8 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_GREATER_H -#define LUCI_INTERPRETER_KERNELS_GREATER_H +#ifndef LUCI_INTERPRETER_KERNELS_SELECT_H +#define LUCI_INTERPRETER_KERNELS_SELECT_H #include "core/Kernel.h" @@ -25,13 +25,14 @@ namespace luci_interpreter namespace kernels { -class Greater : public Kernel +class Select : public Kernel { public: - Greater(const Tensor *x, const Tensor *y, Tensor *output); + Select(const Tensor *cond, const Tensor *t, const Tensor *e, Tensor *output); - const Tensor *x() const { return _inputs[0]; } - const Tensor *y() const { return _inputs[1]; } + const Tensor *condition() const { return _inputs[0]; } + const Tensor *t() const { return _inputs[1]; } + const Tensor *e() const { return _inputs[2]; } Tensor *output() const { return _outputs[0]; } void configure() override; @@ -39,17 +40,16 @@ public: private: void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; + // for SelectV2 + bool _requires_broadcast = false; + // True if input condition is scalar or input condition has rank one and + // matches the first dimension of other inputs. + bool _has_low_rank_input_condition = false; }; } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_GREATER_H +#endif // LUCI_INTERPRETER_KERNELS_SELECT_H diff --git a/compiler/luci-interpreter/src/kernels/Select.test.cpp b/compiler/luci-interpreter/src/kernels/Select.test.cpp new file mode 100644 index 0000000..f74d18d --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Select.test.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 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/Select.h" +#include "kernels/TestUtils.h" + +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class SelectTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +std::vector c_data{ + 1, 1, 1, // Row 1 + 0, 0, 0, // Row 2 +}; + +std::vector t_data{ + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 +}; + +std::vector e_data{ + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 +}; + +std::vector ref_output_data{ + 0.5, 0.7, 0.9, // Row 1 + -1, 0, 1, // Row 2 +}; + +TEST_F(SelectTest, FloatSimple) +{ + Tensor c_tensor = makeInputTensor({2, 3}, c_data, _memory_manager.get()); + Tensor t_tensor = makeInputTensor({2, 3}, t_data, _memory_manager.get()); + Tensor e_tensor = makeInputTensor({2, 3}, e_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Select kernel(&c_tensor, &t_tensor, &e_tensor, &output_tensor); + kernel.configure(); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + EXPECT_THAT(extractTensorData(output_tensor), + ::testing::ElementsAreArray(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); +} + +TEST_F(SelectTest, Invalid_C_Type_NEG) +{ + std::vector i_c_data{ + 1, 1, 1, // Row 1 + 0, 0, 0, // Row 2 + }; + + Tensor c_tensor = makeInputTensor({2, 3}, i_c_data, _memory_manager.get()); + Tensor t_tensor = makeInputTensor({2, 3}, t_data, _memory_manager.get()); + Tensor e_tensor = makeInputTensor({2, 3}, e_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + Select kernel(&c_tensor, &t_tensor, &e_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(SelectTest, Invalid_O_Type_NEG) +{ + Tensor c_tensor = makeInputTensor({2, 3}, c_data, _memory_manager.get()); + Tensor t_tensor = makeInputTensor({2, 3}, t_data, _memory_manager.get()); + Tensor e_tensor = makeInputTensor({2, 3}, e_data, _memory_manager.get()); + Tensor output_tensor = makeOutputTensor(DataType::BOOL); + + Select kernel(&c_tensor, &t_tensor, &e_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Sub.cpp b/compiler/luci-interpreter/src/kernels/Sub.cpp index 24b6a72..1fd583c 100644 --- a/compiler/luci-interpreter/src/kernels/Sub.cpp +++ b/compiler/luci-interpreter/src/kernels/Sub.cpp @@ -148,7 +148,7 @@ void Sub::evalQuantized() const if (need_broadcast) { - tflite::reference_ops::BroadcastSubSlow( + tflite::reference_ops::BroadcastQuantSubSlow( params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), getTensorData(input2()), getTensorShape(output()), getTensorData(output())); } diff --git a/compiler/luci-interpreter/src/kernels/Sum.cpp b/compiler/luci-interpreter/src/kernels/Sum.cpp new file mode 100644 index 0000000..645f02c --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Sum.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023 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/Sum.h" + +#include "kernels/Utils.h" + +#include + +#include + +namespace luci_interpreter +{ +namespace kernels +{ + +// Returns the number of axes that will be reduced. Removes duplicates. +static int getAxisReductionCount(const int32_t *axes_data, int num_axes, int input_num_dims) +{ + int reduction_count = num_axes; + for (int i = 0; i < num_axes; ++i) + { + int current = axes_data[i] >= 0 ? axes_data[i] : axes_data[i] + input_num_dims; + assert(current >= 0 && current < input_num_dims); + for (int j = 0; j < i; j++) + { + int previous = axes_data[j] >= 0 ? axes_data[j] : axes_data[j] + input_num_dims; + // This checks for duplicate axis + if (current == previous) + { + --reduction_count; + break; + } + } + } + return reduction_count; +} + +static Shape getOutputShape(const Shape &input_shape, const int32_t *axes_data, int num_axes, + bool keep_dims) +{ + int input_num_dims = input_shape.num_dims(); + if (input_num_dims == 0) + { + return Shape(0); + } + + if (keep_dims) + { + Shape output_shape(input_num_dims); + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + is_axis = true; + break; + } + } + if (is_axis) + { + output_shape.dim(idx) = 1; + } + else + { + output_shape.dim(idx) = input_shape.dim(idx); + } + } + return output_shape; + } + else + { + int num_reduce_axes = getAxisReductionCount(axes_data, num_axes, input_num_dims); + Shape output_shape(input_num_dims - num_reduce_axes); + int num_skip_axes = 0; + for (int idx = 0; idx < input_num_dims; ++idx) + { + bool is_axis = false; + for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx) + { + if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx) + { + ++num_skip_axes; + is_axis = true; + break; + } + } + if (!is_axis) + { + output_shape.dim(idx - num_skip_axes) = input_shape.dim(idx); + } + } + return output_shape; + } +} + +Sum::Sum(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, const ReducerParams ¶ms) + : KernelWithParams({input, axes}, {output, temp_index, resolved_axes}, params) +{ +} + +void Sum::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32); + + const Shape &input_shape = input()->shape(); + int input_num_dims = input_shape.num_dims(); + + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + LUCI_INTERPRETER_CHECK(num_axes <= 4); + + // We compute shapes of outputs in configure, assuming that outputs have + // static shape + // TODO Support dynamic shape + Shape output_shape = getOutputShape(input_shape, axes_data, num_axes, _params.keep_dims); + output()->resize(output_shape); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + + temp_index->resize(Shape(input_num_dims)); + resolved_axes->resize(Shape(num_axes)); +} + +void Sum::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Sum::evalFloat() const +{ + const auto *axes_data = getTensorData(axes()); + int num_axes = axes()->shape().num_elements(); + + auto temp_index = getOutputTensors()[1]; + auto resolved_axes = getOutputTensors()[2]; + + int num_resolved_axis = 0; + LUCI_INTERPRETER_CHECK( + tflite::reference_ops::ResolveAxis(input()->shape().num_dims(), axes_data, num_axes, + getTensorData(resolved_axes), &num_resolved_axis)); + + float init_value = 0.0; + tflite::reference_ops::ReduceGeneric( + getTensorData(input()), getTensorShape(input()).DimsData(), input()->shape().num_dims(), + getTensorData(output()), getTensorShape(output()).DimsData(), + output()->shape().num_dims(), axes_data, num_axes, _params.keep_dims, + getTensorData(temp_index), getTensorData(resolved_axes), init_value, + [](const float current, const float in) -> float { return current + in; }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Shape.h b/compiler/luci-interpreter/src/kernels/Sum.h similarity index 63% rename from onert-micro/luci-interpreter/src/kernels/Shape.h rename to compiler/luci-interpreter/src/kernels/Sum.h index dcf526d..290e0da 100644 --- a/onert-micro/luci-interpreter/src/kernels/Shape.h +++ b/compiler/luci-interpreter/src/kernels/Sum.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,33 +14,38 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_SHAPE_H -#define LUCI_INTERPRETER_KERNELS_SHAPE_H +#ifndef LUCI_INTERPRETER_KERNELS_SUM_H +#define LUCI_INTERPRETER_KERNELS_SUM_H #include "core/Kernel.h" #include "core/KernelParams.h" +#include + namespace luci_interpreter { namespace kernels { -class ShapeKernel : public KernelWithParams +class Sum : public KernelWithParams { public: - ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms); + // TODO Add temp_sum to support quantized kernels + Sum(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, + Tensor *resolved_axes, const ReducerParams ¶ms); const Tensor *input() const { return _inputs[0]; } + const Tensor *axes() const { return _inputs[1]; } Tensor *output() const { return _outputs[0]; } void configure() override; void execute() const override; private: - template void evalInt() const; + void evalFloat() const; }; } // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_SHAPE_H +#endif // LUCI_INTERPRETER_KERNELS_SUM_H diff --git a/compiler/luci-interpreter/src/kernels/Sum.test.cpp b/compiler/luci-interpreter/src/kernels/Sum.test.cpp new file mode 100644 index 0000000..e2dc301 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Sum.test.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 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/Sum.h" +#include "kernels/TestUtils.h" +#include "luci_interpreter/TestMemoryManager.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +class SumTest : public ::testing::Test +{ +protected: + void SetUp() override { _memory_manager = std::make_unique(); } + + std::unique_ptr _memory_manager; +}; + +TEST_F(SumTest, FloatNotKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{1, 0}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = false; + + Sum kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{144, 156}; + std::initializer_list ref_output_shape{2}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(SumTest, FloatKeepDims) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{0, 2}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = true; + + Sum kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, params); + kernel.configure(); + _memory_manager->allocate_memory(temp_index); + _memory_manager->allocate_memory(resolved_axes); + _memory_manager->allocate_memory(output_tensor); + kernel.execute(); + + std::vector ref_output_data{84, 100, 116}; + std::initializer_list ref_output_shape{1, 3, 1}; + EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); +} + +TEST_F(SumTest, Input_Output_Type_NEG) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{0, 2}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::U8); + + ReducerParams params{}; + params.keep_dims = true; + + Sum kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, params); + + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST_F(SumTest, Invalid_Axes_Type_NEG) +{ + std::vector input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0}; + + std::vector axis_data{0, 2}; + Tensor input_tensor = + makeInputTensor({4, 3, 2}, input_data, _memory_manager.get()); + Tensor axis_tensor = makeInputTensor({2}, axis_data, _memory_manager.get()); + Tensor temp_index(DataType::S32, Shape({}), {}, ""); + Tensor resolved_axes(DataType::S32, Shape({}), {}, ""); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + ReducerParams params{}; + params.keep_dims = true; + + Sum kernel(&input_tensor, &axis_tensor, &output_tensor, &temp_index, &resolved_axes, params); + + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp index 4856e1b..8e9cfc6 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp @@ -54,6 +54,7 @@ void Check(std::initializer_list output_shape_shape, params.padding = padding; params.stride_height = stride_height; params.stride_width = stride_width; + params.activation = luci::FusedActFunc::NONE; if (bias_data.size() != 0) { @@ -164,6 +165,7 @@ TEST(TransposeConvTest, UInt8) params.padding = Padding::VALID; params.stride_height = 2; params.stride_width = 2; + params.activation = luci::FusedActFunc::NONE; TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, &output_tensor, &scratch_tensor, params); @@ -232,6 +234,7 @@ TEST(TransposeConvTest, UInt8_CWQ) params.padding = Padding::VALID; params.stride_height = 2; params.stride_width = 2; + params.activation = luci::FusedActFunc::NONE; TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, &output_tensor, &scratch_tensor, params); @@ -278,6 +281,7 @@ TEST(TransposeConvTest, SInt16) params.padding = Padding::VALID; params.stride_height = 2; params.stride_width = 2; + params.activation = luci::FusedActFunc::NONE; TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, &output_tensor, &scratch_tensor, params); @@ -336,6 +340,7 @@ TEST(TransposeConvTest, SInt16_CWQ_weights) params.padding = Padding::VALID; params.stride_height = 2; params.stride_width = 2; + params.activation = luci::FusedActFunc::NONE; TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor, &output_tensor, &scratch_tensor, params); diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp index b221b69..10a01f4 100644 --- a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp +++ b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp @@ -1319,6 +1319,7 @@ TEST_F(KernelBuilderTest, TransposeConv) op->padding(luci::Padding::SAME); op->stride()->h(11); op->stride()->w(13); + op->fusedActivationFunction(luci::FusedActFunc::NONE); auto kernel = buildKernel(op); ASSERT_THAT(kernel, NotNull()); @@ -1331,6 +1332,7 @@ TEST_F(KernelBuilderTest, TransposeConv) EXPECT_THAT(kernel->params().padding, Eq(op->padding())); EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h())); EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); } TEST_F(KernelBuilderTest, Unpack) diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Floor.cpp b/compiler/luci-interpreter/src/loader/nodes/FloorMod.cpp similarity index 52% rename from onert-micro/luci-interpreter/src/loader/nodes/Floor.cpp rename to compiler/luci-interpreter/src/loader/nodes/FloorMod.cpp index 59164ce..a4852f1 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/Floor.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/FloorMod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,22 @@ #include "Builders.h" -#include "kernels/Floor.h" +#include "kernels/FloorMod.h" namespace luci_interpreter { -std::unique_ptr build_kernel_CircleFloor(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +std::unique_ptr build_kernel_CircleFloorMod(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) { - assert(inputs.size() == 1); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); + const Tensor *x = helper.getInputTensor(node->x()); + const Tensor *y = helper.getInputTensor(node->y()); + Tensor *output = helper.getOutputTensor(node); - return std::make_unique(input, output); + return std::make_unique(x, y, output); } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/PRelu.cpp b/compiler/luci-interpreter/src/loader/nodes/Gelu.cpp similarity index 52% rename from onert-micro/luci-interpreter/src/loader/nodes/PRelu.cpp rename to compiler/luci-interpreter/src/loader/nodes/Gelu.cpp index 6adc549..fc77a58 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/PRelu.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Gelu.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +16,23 @@ #include "Builders.h" -#include "kernels/PRelu.h" +#include "kernels/Gelu.h" namespace luci_interpreter { -std::unique_ptr build_kernel_CirclePRelu(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +std::unique_ptr build_kernel_CircleGelu(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) { - assert(inputs.size() == 2); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); - const Tensor *input = inputs.at(0); - const Tensor *alpha = inputs.at(1); - Tensor *output = outputs.at(0); + GeluParams params{}; + params.approximate = node->approximate(); - return std::make_unique(input, alpha, output); + return std::make_unique(input, output, params); } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp b/compiler/luci-interpreter/src/loader/nodes/HardSwish.cpp similarity index 56% rename from onert-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp rename to compiler/luci-interpreter/src/loader/nodes/HardSwish.cpp index ca4f26c..2e62f24 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/LessEqual.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/HardSwish.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,20 @@ #include "Builders.h" -#include "kernels/LessEqual.h" +#include "kernels/HardSwish.h" namespace luci_interpreter { -std::unique_ptr build_kernel_CircleLessEqual(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) +std::unique_ptr build_kernel_CircleHardSwish(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) { - assert(inputs.size() == 2); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); + const Tensor *input = helper.getInputTensor(node->features()); + Tensor *output = helper.getOutputTensor(node); - return std::make_unique(x, y, output); + return std::make_unique(input, output); } - } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Exp.cpp b/compiler/luci-interpreter/src/loader/nodes/Log.cpp similarity index 56% rename from onert-micro/luci-interpreter/src/loader/nodes/Exp.cpp rename to compiler/luci-interpreter/src/loader/nodes/Log.cpp index 401b6d0..048e310 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/Exp.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Log.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,21 @@ #include "Builders.h" -#include "kernels/Exp.h" +#include "kernels/Log.h" namespace luci_interpreter { -std::unique_ptr build_kernel_CircleExp(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +std::unique_ptr build_kernel_CircleLog(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) { - assert(inputs.size() == 1); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); + const Tensor *input = helper.getInputTensor(node->x()); + Tensor *output = helper.getOutputTensor(node); - return std::make_unique(input, output); + return std::make_unique(input, output); } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Cast.cpp b/compiler/luci-interpreter/src/loader/nodes/Select.cpp similarity index 50% rename from onert-micro/luci-interpreter/src/loader/nodes/Cast.cpp rename to compiler/luci-interpreter/src/loader/nodes/Select.cpp index 441dacb..a0f1804 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/Cast.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/Select.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,23 @@ #include "Builders.h" -#include "kernels/Cast.h" +#include "kernels/Select.h" namespace luci_interpreter { -std::unique_ptr build_kernel_CircleCast(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +std::unique_ptr build_kernel_CircleSelect(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) { - assert(inputs.size() == 1); + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 3); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); + const Tensor *c = helper.getInputTensor(node->condition()); + const Tensor *t = helper.getInputTensor(node->t()); + const Tensor *e = helper.getInputTensor(node->e()); + Tensor *output = helper.getOutputTensor(node); - return std::make_unique(input, output); + return std::make_unique(c, t, e, output); } } // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/loader/nodes/Sum.cpp b/compiler/luci-interpreter/src/loader/nodes/Sum.cpp new file mode 100644 index 0000000..6dfe362 --- /dev/null +++ b/compiler/luci-interpreter/src/loader/nodes/Sum.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Builders.h" + +#include "kernels/Sum.h" + +namespace luci_interpreter +{ + +std::unique_ptr build_kernel_CircleSum(const luci::CircleNode *circle_node, + KernelBuilderHelper &helper) +{ + const auto *node = loco::must_cast(circle_node); + assert(node->arity() == 2); + + const Tensor *input = helper.getInputTensor(node->input()); + const Tensor *axes = helper.getInputTensor(node->reduction_indices()); + Tensor *output = helper.getOutputTensor(node); + + auto temp_index_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + temp_index_unique->set_observable(false); + temp_index_unique->set_data_buffer(nullptr); + Tensor *temp_index = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(temp_index_unique)); + + auto resolved_axes_unique = + std::make_unique(DataType::S32, Shape({}), AffineQuantization{}, ""); + resolved_axes_unique->set_observable(false); + resolved_axes_unique->set_data_buffer(nullptr); + Tensor *resolved_axes = + helper.getRuntimeGraph(node->graph())->addTensor(std::move(resolved_axes_unique)); + + ReducerParams params{}; + params.keep_dims = node->keep_dims(); + + return std::make_unique(input, axes, output, temp_index, resolved_axes, params); +} + +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp b/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp index d773e30..72d1aec 100644 --- a/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/loader/nodes/TransposeConv.cpp @@ -47,6 +47,13 @@ std::unique_ptr build_kernel_CircleTransposeConv(const luci::CircleNode params.padding = node->padding(); params.stride_height = node->stride()->h(); params.stride_width = node->stride()->w(); + params.activation = node->fusedActivationFunction(); + + // TODO support activation + if (params.activation != luci::FusedActFunc::NONE) + { + throw std::runtime_error("Unsupported activation of TransposeConv"); + } return std::make_unique(input_sizes, filter, out_backprop, bias, output, tmp, params); diff --git a/compiler/luci-pass-value-test/CMakeLists.txt b/compiler/luci-pass-value-test/CMakeLists.txt index c86d2cd..dcd242c 100644 --- a/compiler/luci-pass-value-test/CMakeLists.txt +++ b/compiler/luci-pass-value-test/CMakeLists.txt @@ -49,18 +49,7 @@ add_test(NAME luci_pass_value_test COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/eval_driver.sh" "${CMAKE_CURRENT_BINARY_DIR}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" "$" ${LUCI_PASS_VALUE_TESTS} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test(NAME luci_pass_value_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/eval_driver.sh" - "${CMAKE_CURRENT_BINARY_DIR}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - "$" - ${LUCI_PASS_VALUE_TESTS} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/luci-pass-value-test/test.lst b/compiler/luci-pass-value-test/test.lst index b0b938c..93634b2 100644 --- a/compiler/luci-pass-value-test/test.lst +++ b/compiler/luci-pass-value-test/test.lst @@ -13,6 +13,7 @@ addeval(Net_Conv_Add_Mul_001 fuse_batchnorm_with_conv) addeval(Net_Conv_Add_Mul_002 fuse_batchnorm_with_conv) addeval(Net_Conv_Min_Max_000 transform_min_max_to_relu6) addeval(Net_Conv_Min_Relu_000 transform_min_relu_to_relu6) +addeval(HardSwish_001 decompose_hardswish) addeval(Net_Conv_PReluGraph_000 fuse_prelu) addeval(Net_Conv_Relu6_000 fuse_activation_function) addeval(Net_Densify_Add_000 fold_densify) diff --git a/compiler/luci-value-test/CMakeLists.txt b/compiler/luci-value-test/CMakeLists.txt index b55f602..be7e881 100644 --- a/compiler/luci-value-test/CMakeLists.txt +++ b/compiler/luci-value-test/CMakeLists.txt @@ -29,7 +29,7 @@ if(NOT CMAKE_CROSSCOMPILING) COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/evalverify.sh" "${CMAKE_CURRENT_BINARY_DIR}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" "$" ${LUCI_VALUE_TESTS} ) @@ -39,34 +39,12 @@ if(NOT CMAKE_CROSSCOMPILING) COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/evalverifytol.sh" "${CMAKE_CURRENT_BINARY_DIR}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" "$" ${LUCI_VALUE_TESTS_TOL} ) endif() - if(ONE_UBUNTU_CODENAME_JAMMY) - add_test(NAME luci_value_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/evalverify.sh" - "${CMAKE_CURRENT_BINARY_DIR}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - "$" - ${LUCI_VALUE_TESTS} - ) - - if(DEFINED LUCI_VALUE_TESTS_TOL) - add_test(NAME luci_value_tol_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/evalverifytol.sh" - "${CMAKE_CURRENT_BINARY_DIR}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - "$" - ${LUCI_VALUE_TESTS_TOL} - ) - endif() - endif(ONE_UBUNTU_CODENAME_JAMMY) - else(NOT CMAKE_CROSSCOMPILING) # NOTE target test is carried out using reference input/output data from host # test results. this is because it would be difficult to prepare diff --git a/compiler/luci-value-test/test.lst b/compiler/luci-value-test/test.lst index 70c3fb1..3368b64 100644 --- a/compiler/luci-value-test/test.lst +++ b/compiler/luci-value-test/test.lst @@ -33,7 +33,7 @@ addeval(Conv2D_000) addeval(Conv2D_001) addeval(Conv2D_002) addeval(Conv2D_003) -addeval(Conv2D_U8_000) +#addeval(Conv2D_U8_000) --> test with tolerance addeval(Conv2D_U8_001) #addeval(Cos_000) addeval(DepthToSpace_000) @@ -54,16 +54,18 @@ addeval(Exp_000) addeval(Floor_000) addeval(FloorDiv_000) addeval(FloorDiv_001) -#addeval(FloorMod_000) -#addeval(FloorMod_001) +addeval(FloorMod_000) +addeval(FloorMod_001) addeval(FullyConnected_000) addeval(FullyConnected_001) addeval(FullyConnected_002) #addeval(FullyConnected_U8_000) addeval(Gather_000) #addeval(GatherNd_000) +addeval(Gelu_000) #addeval(Greater_000) addeval(GreaterEqual_000) +addeval(HardSwish_000) addeval(If_000) addeval(If_001) addeval(L2Normalize_000) @@ -73,7 +75,7 @@ addeval(LeakyRelu_000) addeval(Less_000) addeval(LessEqual_000) addeval(LocalResponseNormalization_000) -#addeval(Log_000) +addeval(Log_000) addeval(LogicalAnd_000) addeval(LogicalNot_000) addeval(LogicalOr_000) @@ -132,9 +134,9 @@ addeval(ResizeNearestNeighbor_000) addeval(Rsqrt_000) #addeval(ScatterNd_000) #addeval(SegmentSum_000) -#addeval(Select_000) -#addeval(Select_001) -#addeval(Select_002) +addeval(Select_000) +addeval(Select_001) +addeval(Select_002) #addeval(SelectV2_000) #addeval(SelectV2_001) #addeval(SelectV2_002) @@ -165,8 +167,8 @@ addeval(StridedSlice_003) addeval(StridedSlice_004) addeval(Sub_000) addeval(Sub_U8_000) -#addeval(Sum_000) -#addeval(Sum_001) +addeval(Sum_000) +addeval(Sum_001) addeval(Tanh_000) #addeval(Tile_000) #addeval(Tile_U8_000) @@ -197,5 +199,8 @@ addeval(Part_While_001) # Tests with tolerance addevaltol(SVDF_000 8e-3 8e-3) addevaltol(SVDF_001 8e-3 8e-3) +# TODO fix Conv2D_U8_000 to test without tolerenace +# refer https://github.com/Samsung/ONE/issues/11255#issuecomment-1685424361 +addeval(Conv2D_U8_000 1 1) # refer https://github.com/Samsung/ONE/issues/10438 addevaltol(YUV_TO_RGB_U8_000 1 1) diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt index f46181e..fb0e20e 100644 --- a/compiler/luci/export/CMakeLists.txt +++ b/compiler/luci/export/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(luci_export PUBLIC include) target_link_libraries(luci_export PRIVATE luci_lang) target_link_libraries(luci_export PRIVATE luci_service) target_link_libraries(luci_export PRIVATE luci_pass) -target_link_libraries(luci_export PRIVATE mio_circle04) +target_link_libraries(luci_export PRIVATE mio_circle06) target_link_libraries(luci_export PRIVATE luci_env) target_link_libraries(luci_export PRIVATE luci_log) target_link_libraries(luci_export PRIVATE luci_logex) @@ -36,6 +36,6 @@ target_include_directories(luci_export_test PRIVATE src) target_link_libraries(luci_export_test luci_export) target_link_libraries(luci_export_test luci_plan) target_link_libraries(luci_export_test luci_lang) -target_link_libraries(luci_export_test mio_circle04) +target_link_libraries(luci_export_test mio_circle06) target_link_libraries(luci_export_test luci_env) target_link_libraries(luci_export_test oops) diff --git a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h index 7516197..811373f 100644 --- a/compiler/luci/export/src/CircleBuiltinTypesExtractor.h +++ b/compiler/luci/export/src/CircleBuiltinTypesExtractor.h @@ -187,6 +187,10 @@ public: { return circle::CreateGatherNdOptions(_builder).Union(); } + flatbuffers::Offset visit(luci::CircleGelu *node) + { + return circle::CreateGeluOptions(_builder, node->approximate()).Union(); + } flatbuffers::Offset visit(luci::CircleGreater *) { return circle::CreateGreaterOptions(_builder).Union(); @@ -195,6 +199,7 @@ public: { return circle::CreateGreaterEqualOptions(_builder).Union(); } + flatbuffers::Offset visit(luci::CircleHardSwish *) { return _no_option; } flatbuffers::Offset visit(luci::CircleIf *node) { return circle::CreateIfOptions(_builder, node->then_branch(), node->else_branch()).Union(); @@ -480,7 +485,8 @@ public: flatbuffers::Offset visit(luci::CircleTransposeConv *node) { return circle::CreateTransposeConvOptions(_builder, getOpPadding(node->padding()), - node->stride()->w(), node->stride()->h()) + node->stride()->w(), node->stride()->h(), + to_circle_actfunc(node->fusedActivationFunction())) .Union(); } flatbuffers::Offset visit(luci::CircleUnidirectionalSequenceLSTM *node) diff --git a/compiler/luci/export/src/CircleOps.lst b/compiler/luci/export/src/CircleOps.lst index 8a75ef7..a047f29 100644 --- a/compiler/luci/export/src/CircleOps.lst +++ b/compiler/luci/export/src/CircleOps.lst @@ -49,8 +49,10 @@ CIRCLE_NODE(CircleFloorMod, BuiltinOperator_FLOOR_MOD, BuiltinOptions_FloorModOp CIRCLE_NODE(CircleFullyConnected, BuiltinOperator_FULLY_CONNECTED, BuiltinOptions_FullyConnectedOptions) CIRCLE_NODE(CircleGather, BuiltinOperator_GATHER, BuiltinOptions_GatherOptions) CIRCLE_NODE(CircleGatherNd, BuiltinOperator_GATHER_ND, BuiltinOptions_GatherNdOptions) +CIRCLE_NODE(CircleGelu, BuiltinOperator_GELU, BuiltinOptions_GeluOptions) CIRCLE_NODE(CircleGreater, BuiltinOperator_GREATER, BuiltinOptions_GreaterOptions) CIRCLE_NODE(CircleGreaterEqual, BuiltinOperator_GREATER_EQUAL, BuiltinOptions_GreaterEqualOptions) +CIRCLE_NODE(CircleHardSwish, BuiltinOperator_HARD_SWISH, BuiltinOptions_NONE) CIRCLE_NODE(CircleIf, BuiltinOperator_IF, BuiltinOptions_IfOptions) CIRCLE_NODE(CircleL2Normalize, BuiltinOperator_L2_NORMALIZATION, BuiltinOptions_L2NormOptions) CIRCLE_NODE(CircleL2Pool2D, BuiltinOperator_L2_POOL_2D, BuiltinOptions_Pool2DOptions) diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt index bc0a00b..2e7e881 100644 --- a/compiler/luci/import/CMakeLists.txt +++ b/compiler/luci/import/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(luci_import PUBLIC include) target_link_libraries(luci_import PUBLIC luci_lang) target_link_libraries(luci_import PUBLIC luci_profile) target_link_libraries(luci_import PUBLIC luci_plan) -target_link_libraries(luci_import PUBLIC mio_circle04) +target_link_libraries(luci_import PUBLIC mio_circle06) target_link_libraries(luci_import PRIVATE luci_env) target_link_libraries(luci_import PRIVATE luci_log) target_link_libraries(luci_import PRIVATE luci_logex) @@ -20,7 +20,7 @@ target_link_libraries(luci_import PRIVATE nncc_common) target_link_libraries(luci_import PRIVATE locop) target_link_libraries(luci_import PRIVATE foder) target_link_libraries(luci_import PRIVATE oops) -target_link_libraries(luci_import PRIVATE mio_circle04_helper) +target_link_libraries(luci_import PRIVATE mio_circle06_helper) install(TARGETS luci_import DESTINATION lib) install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*.h") diff --git a/compiler/luci/import/include/luci/Import/NodeBuilder.h b/compiler/luci/import/include/luci/Import/NodeBuilder.h index 440b491..05f533f 100644 --- a/compiler/luci/import/include/luci/Import/NodeBuilder.h +++ b/compiler/luci/import/include/luci/Import/NodeBuilder.h @@ -42,6 +42,7 @@ class NodeBuilderBase public: virtual CircleNode *build(TensorIndex tensor_idx, GraphBuilderContext *context) const = 0; virtual NodeBuilderType builder_type() const = 0; + virtual ~NodeBuilderBase() = default; }; /** diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h index a4a6d7c..e8c8d0a 100644 --- a/compiler/luci/import/include/luci/Import/Nodes.h +++ b/compiler/luci/import/include/luci/Import/Nodes.h @@ -52,8 +52,10 @@ #include "Nodes/CircleFullyConnected.h" #include "Nodes/CircleGather.h" #include "Nodes/CircleGatherNd.h" +#include "Nodes/CircleGelu.h" #include "Nodes/CircleGreater.h" #include "Nodes/CircleGreaterEqual.h" +#include "Nodes/CircleHardSwish.h" #include "Nodes/CircleIf.h" #include "Nodes/CircleInstanceNorm.h" #include "Nodes/CircleL2Normalize.h" diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleGelu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleGelu.h new file mode 100644 index 0000000..9be266f --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleGelu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GELU_H__ +#define __LUCI_IMPORT_OP_CIRCLE_GELU_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleGeluGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_GELU_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleHardSwish.h b/compiler/luci/import/include/luci/Import/Nodes/CircleHardSwish.h new file mode 100644 index 0000000..7aeb029 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleHardSwish.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_HARDSWISH_H__ +#define __LUCI_IMPORT_OP_CIRCLE_HARDSWISH_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleHardSwishGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_HARDSWISH_H__ diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp index d3b52aa..9c86832 100644 --- a/compiler/luci/import/src/GraphBuilderRegistry.cpp +++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp @@ -61,13 +61,15 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnectedGraphBuilder); // 9 CIRCLE_NODE(GATHER, CircleGatherGraphBuilder); // 36 CIRCLE_NODE(GATHER_ND, CircleGatherNdGraphBuilder); // 107 + CIRCLE_NODE(GELU, CircleGeluGraphBuilder); // 150 CIRCLE_NODE(GREATER, CircleGreaterGraphBuilder); // 61 CIRCLE_NODE(GREATER_EQUAL, CircleGreaterEqualGraphBuilder); // 62 + CIRCLE_NODE(HARD_SWISH, CircleHardSwishGraphBuilder); // 117 CIRCLE_NODE(IF, CircleIfGraphBuilder); // 118 CIRCLE_NODE(INSTANCE_NORM, CircleInstanceNormGraphBuilder); // 254 CIRCLE_NODE(L2_NORMALIZATION, CircleL2NormalizeGraphBuilder); // 11 CIRCLE_NODE(L2_POOL_2D, CircleL2Pool2DGraphBuilder); // 12 - CIRCLE_NODE(LEAKY_RELU, CircleLeakyReluGraphBuilder); // 98, + CIRCLE_NODE(LEAKY_RELU, CircleLeakyReluGraphBuilder); // 98 CIRCLE_NODE(LESS, CircleLessGraphBuilder); // 58 CIRCLE_NODE(LESS_EQUAL, CircleLessEqualGraphBuilder); // 63 CIRCLE_NODE(LOCAL_RESPONSE_NORMALIZATION, CircleLocalResponseNormalizationGraphBuilder); // 13 @@ -86,16 +88,16 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(MIRROR_PAD, CircleMirrorPadGraphBuilder); // 100 CIRCLE_NODE(MUL, CircleMulGraphBuilder); // 18 CIRCLE_NODE(NEG, CircleNegGraphBuilder); // 59 - CIRCLE_NODE(NON_MAX_SUPPRESSION_V4, CircleNonMaxSuppressionV4GraphBuilder); // 120, - CIRCLE_NODE(NON_MAX_SUPPRESSION_V5, CircleNonMaxSuppressionV5GraphBuilder); // 121, + CIRCLE_NODE(NON_MAX_SUPPRESSION_V4, CircleNonMaxSuppressionV4GraphBuilder); // 120 + CIRCLE_NODE(NON_MAX_SUPPRESSION_V5, CircleNonMaxSuppressionV5GraphBuilder); // 121 CIRCLE_NODE(NOT_EQUAL, CircleNotEqualGraphBuilder); // 72 CIRCLE_NODE(ONE_HOT, CircleOneHotGraphBuilder); // 85 CIRCLE_NODE(PACK, CirclePackGraphBuilder); // 83 CIRCLE_NODE(PAD, CirclePadGraphBuilder); // 34 CIRCLE_NODE(PADV2, CirclePadV2GraphBuilder); // 60 CIRCLE_NODE(POW, CirclePowGraphBuilder); // 78 - CIRCLE_NODE(PRELU, CirclePReluGraphBuilder); // 54, - CIRCLE_NODE(QUANTIZE, CircleQuantizeGraphBuilder); // 114, + CIRCLE_NODE(PRELU, CirclePReluGraphBuilder); // 54 + CIRCLE_NODE(QUANTIZE, CircleQuantizeGraphBuilder); // 114 CIRCLE_NODE(RANGE, CircleRangeGraphBuilder); // 96 CIRCLE_NODE(RANK, CircleRankGraphBuilder); // 110 CIRCLE_NODE(REDUCE_ANY, CircleReduceAnyGraphBuilder); // 91 @@ -160,7 +162,6 @@ GraphBuilderRegistry::GraphBuilderRegistry() // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46, // BuiltinOperator_DELEGATE = 51, // BuiltinOperator_ARG_MAX = 56, - // BuiltinOperator_HARD_SWISH = 117, // Register builders for nodes which not handles in builders registered above. #define CIRCLE_NODE(CLASS) add(std::make_unique()) diff --git a/compiler/luci/import/src/Nodes/CircleGelu.cpp b/compiler/luci/import/src/Nodes/CircleGelu.cpp new file mode 100644 index 0000000..89b325f --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleGelu.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/CircleGelu.h" + +#include + +#include + +namespace luci +{ + +bool CircleGeluGraphBuilder::validate(const ValidateArgs &args) const +{ + return GraphBuilder::validate(args, 1); +} + +CircleNode *CircleGeluGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + node->features(inputs.at(0)); + + const auto *options = op.builtin_options.AsGeluOptions(); + node->approximate(options->approximate); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleHardSwish.cpp b/compiler/luci/import/src/Nodes/CircleHardSwish.cpp new file mode 100644 index 0000000..47fc1c9 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleHardSwish.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/CircleHardSwish.h" + +#include + +#include + +namespace luci +{ + +bool CircleHardSwishGraphBuilder::validate(const ValidateArgs &args) const +{ + return GraphBuilder::validate(args, 1); +} + +CircleNode *CircleHardSwishGraphBuilder::build_node(const circle::OperatorT &, + const std::vector &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create(); + node->features(inputs.at(0)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleReshape.cpp b/compiler/luci/import/src/Nodes/CircleReshape.cpp index 3421620..12da54e 100644 --- a/compiler/luci/import/src/Nodes/CircleReshape.cpp +++ b/compiler/luci/import/src/Nodes/CircleReshape.cpp @@ -30,7 +30,7 @@ bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const if (args.op.outputs.size() != 1) return false; - // for two inputs, check if type is S32 + // for two inputs, check if type is S32 or S64 if (args.op.inputs.size() == 2) { const auto &inputs = args.op.inputs; @@ -38,9 +38,8 @@ bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const const auto tensor_in = tensors.at(inputs.at(1)); assert(tensor_in != nullptr); - // NOTE fix this if there is any other case - // TensorFlow lite and circle only supports S32 - if (tensor_in->type() != circle::TensorType::TensorType_INT32) + if (tensor_in->type() != circle::TensorType::TensorType_INT32 && + tensor_in->type() != circle::TensorType::TensorType_INT64) return false; } diff --git a/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp index 01a28cb..62326f4 100644 --- a/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp +++ b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp @@ -74,6 +74,7 @@ CircleNode *CircleTransposeConvGraphBuilder::build_node(const circle::OperatorT node->padding(luci_padding(options->padding)); node->stride()->w(options->stride_w); node->stride()->h(options->stride_h); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); return node; } diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h index 901f1cb..d643b08 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h @@ -49,8 +49,10 @@ #include "Nodes/CircleFullyConnected.h" #include "Nodes/CircleGather.h" #include "Nodes/CircleGatherNd.h" +#include "Nodes/CircleGelu.h" #include "Nodes/CircleGreater.h" #include "Nodes/CircleGreaterEqual.h" +#include "Nodes/CircleHardSwish.h" #include "Nodes/CircleIf.h" #include "Nodes/CircleL2Normalize.h" #include "Nodes/CircleL2Pool2D.h" diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst index f227a03..1646909 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.lst +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst @@ -47,8 +47,10 @@ CIRCLE_NODE(FLOOR_MOD, CircleFloorMod) CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnected) CIRCLE_NODE(GATHER, CircleGather) CIRCLE_NODE(GATHER_ND, CircleGatherNd) +CIRCLE_NODE(GELU, CircleGelu) CIRCLE_NODE(GREATER, CircleGreater) CIRCLE_NODE(GREATER_EQUAL, CircleGreaterEqual) +CIRCLE_NODE(HARD_SWISH, CircleHardSwish) CIRCLE_NODE(IF, CircleIf) CIRCLE_NODE(L2_NORMALIZATION, CircleL2Normalize) CIRCLE_NODE(L2_POOL_2D, CircleL2Pool2D) diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGelu.h new file mode 100644 index 0000000..badfec7 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGelu.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CIRCLEGELU_H__ +#define __LUCI_IR_CIRCLEGELU_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief GELU in Circle + */ +class CircleGelu final : public FixedArityNode<1, CircleNodeImpl> +{ +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } + +public: + bool approximate(void) const { return _approximate; } + void approximate(bool arg) { _approximate = arg; } + +private: + bool _approximate{false}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEGELU_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleHardSwish.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleHardSwish.h new file mode 100644 index 0000000..18652a0 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleHardSwish.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CIRCLEHARDSWISH_H__ +#define __LUCI_IR_CIRCLEHARDSWISH_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief HardSwish in Circle + */ +class CircleHardSwish final : public FixedArityNode<1, CircleNodeImpl> +{ +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEHARDSWISH_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h index 5ae41c0..8c6f04a 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h @@ -35,6 +35,7 @@ namespace luci */ class CircleTransposeConv final : public FixedArityNode<4, CircleNodeImpl>, + public CircleNodeMixin, public CircleNodeMixin { public: diff --git a/compiler/luci/lang/src/Nodes/CircleGelu.test.cpp b/compiler/luci/lang/src/Nodes/CircleGelu.test.cpp new file mode 100644 index 0000000..b10a2b4 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleGelu.test.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/CircleGelu.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include + +TEST(CircleGeluTest, constructor) +{ + luci::CircleGelu gelu_node; + + ASSERT_EQ(luci::CircleDialect::get(), gelu_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::GELU, gelu_node.opcode()); + + ASSERT_EQ(nullptr, gelu_node.features()); + + ASSERT_EQ(false, gelu_node.approximate()); +} + +TEST(CircleGeluTest, input_NEG) +{ + luci::CircleGelu gelu_node; + luci::CircleGelu node; + + gelu_node.features(&node); + ASSERT_NE(nullptr, gelu_node.features()); + + gelu_node.features(nullptr); + ASSERT_EQ(nullptr, gelu_node.features()); + + gelu_node.approximate(true); + ASSERT_NE(false, gelu_node.approximate()); +} + +TEST(CircleGeluTest, arity_NEG) +{ + luci::CircleGelu gelu_node; + + ASSERT_NO_THROW(gelu_node.arg(0)); + ASSERT_THROW(gelu_node.arg(1), std::out_of_range); +} + +TEST(CircleGeluTest, visit_mutable_NEG) +{ + struct TestVisitor final : public luci::CircleNodeMutableVisitor + { + }; + + luci::CircleGelu gelu_node; + + TestVisitor tv; + ASSERT_THROW(gelu_node.accept(&tv), std::exception); +} + +TEST(CircleGeluTest, visit_NEG) +{ + struct TestVisitor final : public luci::CircleNodeVisitor + { + }; + + luci::CircleGelu gelu_node; + + TestVisitor tv; + ASSERT_THROW(gelu_node.accept(&tv), std::exception); +} diff --git a/compiler/luci/lang/src/Nodes/CircleHardSwish.test.cpp b/compiler/luci/lang/src/Nodes/CircleHardSwish.test.cpp new file mode 100644 index 0000000..7825ce7 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleHardSwish.test.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/CircleHardSwish.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include + +TEST(CircleHardSwishTest, constructor_P) +{ + luci::CircleHardSwish hard_swish_node; + + ASSERT_EQ(luci::CircleDialect::get(), hard_swish_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::HARD_SWISH, hard_swish_node.opcode()); + + ASSERT_EQ(nullptr, hard_swish_node.features()); +} + +TEST(CircleHardSwishTest, input_NEG) +{ + luci::CircleHardSwish hard_swish_node; + luci::CircleHardSwish node; + + hard_swish_node.features(&node); + ASSERT_NE(nullptr, hard_swish_node.features()); + + hard_swish_node.features(nullptr); + ASSERT_EQ(nullptr, hard_swish_node.features()); +} + +TEST(CircleHardSwishTest, arity_NEG) +{ + luci::CircleHardSwish hard_swish_node; + + ASSERT_NO_THROW(hard_swish_node.arg(0)); + ASSERT_THROW(hard_swish_node.arg(1), std::out_of_range); +} + +TEST(CircleHardSwishTest, visit_mutable_NEG) +{ + struct TestVisitor final : public luci::CircleNodeMutableVisitor + { + }; + + luci::CircleHardSwish hard_swish_node; + + TestVisitor tv; + ASSERT_THROW(hard_swish_node.accept(&tv), std::exception); +} + +TEST(CircleHardSwishTest, visit_NEG) +{ + struct TestVisitor final : public luci::CircleNodeVisitor + { + }; + + luci::CircleHardSwish hard_swish_node; + + TestVisitor tv; + ASSERT_THROW(hard_swish_node.accept(&tv), std::exception); +} diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp index 8409f25..e7f38d0 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilder.cpp @@ -61,6 +61,19 @@ bool CircleNodeSummaryBuilder::build(const loco::Node *node, const locop::Symbol return ss.str(); }; + auto shape_to_str = [](const luci::CircleNode *node) { + std::stringstream ss; + ss << "<"; + for (uint32_t i = 0; i < node->rank(); ++i) + { + if (i) + ss << ","; + ss << (node->dim(i).known() ? node->dim(i).value() : -1); + } + ss << ">"; + return ss.str(); + }; + auto circle_node = loco::must_cast(node); if (const auto builder = create_builder(circle_node)) { @@ -79,7 +92,8 @@ bool CircleNodeSummaryBuilder::build(const loco::Node *node, const locop::Symbol builder->update_status(s); s.opname(circle_opname(circle_node->opcode())); - s.comments().append("[" + circle_node->name() + "] = " + ptr_to_str(node)); + s.comments().append("[" + circle_node->name() + " " + shape_to_str(circle_node) + + "] = " + ptr_to_str(node)); return true; } @@ -154,8 +168,10 @@ CircleNodeSummaryBuilder::create_builder(const luci::CircleNode *node) CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnectedSummaryBuilder) CIRCLE_NODE(GATHER, CircleGatherSummaryBuilder) CIRCLE_NODE(GATHER_ND, CircleGatherNdSummaryBuilder) + CIRCLE_NODE(GELU, CircleGeluSummaryBuilder) CIRCLE_NODE(GREATER, CircleGreaterSummaryBuilder) CIRCLE_NODE(GREATER_EQUAL, CircleGreaterEqualSummaryBuilder) + CIRCLE_NODE(HARD_SWISH, CircleHardSwishSummaryBuilder) CIRCLE_NODE(IF, CircleIfSummaryBuilder) CIRCLE_NODE(INSTANCE_NORM, CircleInstanceNormSummaryBuilder) CIRCLE_NODE(L2_NORMALIZATION, CircleL2NormalizeSummaryBuilder) diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilder.test.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilder.test.cpp index 89ea213..ae76bad 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilder.test.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilder.test.cpp @@ -298,6 +298,7 @@ TEST_F(CircleNodeSummaryBuilderTest, TransposeConv_validate) { luci::CircleTransposeConv node; node.padding(luci::Padding::SAME); + node.fusedActivationFunction(luci::FusedActFunc::RELU); EXPECT_TRUE(mock_build(&node)); } @@ -307,3 +308,10 @@ TEST_F(CircleNodeSummaryBuilderTest, TransposeConv_validate_padding_NEG) node.padding(luci::Padding::UNDEFINED); EXPECT_FALSE(mock_build(&node)); } + +TEST_F(CircleNodeSummaryBuilderTest, TransposeConv_validate_fused_NEG) +{ + luci::CircleTransposeConv node; + node.fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + EXPECT_FALSE(mock_build(&node)); +} diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp b/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp index 42f11be..aba6a86 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilders.cpp @@ -539,6 +539,12 @@ std::vector CircleGatherNdSummaryBuilder::get_input_names(const luc return {"params", "indices"}; } +void CircleGeluSummaryBuilder::build_attributes(const luci::CircleNode *node, locop::NodeSummary &s) +{ + auto gelu = loco::must_cast(node); + s.args().append("approximate", to_str(gelu->approximate())); +} + std::vector CircleIfSummaryBuilder::get_input_names(const luci::CircleNode *node) { auto circle_if = loco::must_cast(node); @@ -1012,6 +1018,8 @@ bool CircleTransposeConvSummaryBuilder::validate(const luci::CircleNode *node) auto transpose_conv = loco::must_cast(node); if (transpose_conv->padding() == luci::Padding::UNDEFINED) return false; + if (transpose_conv->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return false; return true; } @@ -1028,6 +1036,7 @@ void CircleTransposeConvSummaryBuilder::build_attributes(const luci::CircleNode auto transpose_conv = loco::must_cast(node); s.args().append("stride(h,w)", to_str(transpose_conv->stride())); s.args().append("padding", to_str(transpose_conv->padding())); + s.args().append("fused_activation_function", to_str(transpose_conv->fusedActivationFunction())); } std::vector diff --git a/compiler/luci/logex/src/CircleNodeSummaryBuilders.h b/compiler/luci/logex/src/CircleNodeSummaryBuilders.h index f0cac4e..0bdb05d 100644 --- a/compiler/luci/logex/src/CircleNodeSummaryBuilders.h +++ b/compiler/luci/logex/src/CircleNodeSummaryBuilders.h @@ -280,6 +280,12 @@ private: std::vector get_input_names(const luci::CircleNode *); }; +class CircleGeluSummaryBuilder final : public CircleNodeWithFEATURESSummaryBuilder +{ +private: + void build_attributes(const luci::CircleNode *node, locop::NodeSummary &s); +}; + class CircleGreaterSummaryBuilder final : public CircleNodeWithXYSummaryBuilder { }; @@ -288,6 +294,10 @@ class CircleGreaterEqualSummaryBuilder final : public CircleNodeWithXYSummaryBui { }; +class CircleHardSwishSummaryBuilder final : public CircleNodeWithFEATURESSummaryBuilder +{ +}; + class CircleIfSummaryBuilder final : public CircleNodeSummaryBuilder { private: diff --git a/compiler/luci/partition/CMakeLists.txt b/compiler/luci/partition/CMakeLists.txt index f28207d..304ef63 100644 --- a/compiler/luci/partition/CMakeLists.txt +++ b/compiler/luci/partition/CMakeLists.txt @@ -13,7 +13,7 @@ target_link_libraries(luci_partition PUBLIC luci_lang) target_link_libraries(luci_partition PRIVATE luci_service) target_link_libraries(luci_partition PRIVATE luci_log) target_link_libraries(luci_partition PRIVATE luci_logex) -target_link_libraries(luci_partition PRIVATE mio_circle04) +target_link_libraries(luci_partition PRIVATE mio_circle06) target_link_libraries(luci_partition PRIVATE nncc_common) target_link_libraries(luci_partition PRIVATE pepper_csv2vec) target_link_libraries(luci_partition PRIVATE oops) diff --git a/compiler/luci/partition/include/luci/ConnectNode.h b/compiler/luci/partition/include/luci/ConnectNode.h index 2d9d41d..d8cbfc6 100644 --- a/compiler/luci/partition/include/luci/ConnectNode.h +++ b/compiler/luci/partition/include/luci/ConnectNode.h @@ -94,8 +94,10 @@ public: void visit(const luci::CircleFullyConnected *) final; void visit(const luci::CircleGather *) final; void visit(const luci::CircleGatherNd *) final; + void visit(const luci::CircleGelu *) final; void visit(const luci::CircleGreater *) final; void visit(const luci::CircleGreaterEqual *) final; + void visit(const luci::CircleHardSwish *) final; void visit(const luci::CircleIf *) final; void visit(const luci::CircleL2Normalize *) final; void visit(const luci::CircleL2Pool2D *) final; diff --git a/onert-micro/luci-interpreter/src/loader/nodes/While.cpp b/compiler/luci/partition/src/Nodes/CircleGelu.cpp similarity index 53% rename from onert-micro/luci-interpreter/src/loader/nodes/While.cpp rename to compiler/luci/partition/src/Nodes/CircleGelu.cpp index b1f719e..74ef51c 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/While.cpp +++ b/compiler/luci/partition/src/Nodes/CircleGelu.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,25 @@ * limitations under the License. */ -#include "Builders.h" +#include "luci/ConnectNode.h" -#include "kernels/While.h" - -namespace luci_interpreter +namespace { -std::unique_ptr build_kernel_CircleWhile(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +void connect(luci::ConnectNode *cn, const luci::CircleGelu *node) { - // TODO: support IF operation - assert(false && "Not supported now"); + auto *cloned = loco::must_cast(cn->find_clone(node)); + + luci::CircleNode *features = loco::must_cast(node->features()); + + cloned->features(cn->find_clone(features)); } -} // namespace luci_interpreter +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleGelu *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleGelu.test.cpp b/compiler/luci/partition/src/Nodes/CircleGelu.test.cpp new file mode 100644 index 0000000..ebef3f7 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleGelu.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/ConnectNode.h" + +#include "ConnectNode.test.h" + +#include + +#include + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT +{ +public: + NodeGraphlet() = default; +}; + +class TestNodeGraph : public TestIOGraph, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + NodeGraphlet::init(g()); + + node()->features(input()); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Gelu) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(1, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); +} + +TEST(ConnectNodeTest, connect_Gelu_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleHardSwish.cpp b/compiler/luci/partition/src/Nodes/CircleHardSwish.cpp new file mode 100644 index 0000000..d6903f3 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleHardSwish.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleHardSwish *node) +{ + auto *cloned = loco::must_cast(cn->find_clone(node)); + + luci::CircleNode *features = loco::must_cast(node->features()); + + cloned->features(cn->find_clone(features)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleHardSwish *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleHardSwish.test.cpp b/compiler/luci/partition/src/Nodes/CircleHardSwish.test.cpp new file mode 100644 index 0000000..7705973 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleHardSwish.test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/ConnectNode.h" + +#include "ConnectNode.test.h" + +#include + +#include + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT +{ +public: + NodeGraphlet() = default; +}; + +class TestNodeGraph : public TestIOGraph, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + NodeGraphlet::init(g()); + + node()->features(input()); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_HardSwish) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(1, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); +} + +TEST(ConnectNodeTest, connect_HardSwish_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp index 68adaad..7dbdfd9 100644 --- a/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp +++ b/compiler/luci/partition/src/Nodes/CircleTransposeConv.test.cpp @@ -38,6 +38,7 @@ public: NodeGraphletT::init(g); _node->padding(luci::Padding::VALID); + _node->fusedActivationFunction(luci::FusedActFunc::RELU); } }; diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h index d77e89d..6ebacee 100644 --- a/compiler/luci/pass/include/luci/CircleOptimizer.h +++ b/compiler/luci/pass/include/luci/CircleOptimizer.h @@ -63,6 +63,7 @@ public: MakeBatchNormGammaPositive, FuseActivationFunction, FusePRelu, + FuseGelu, ShuffleWeightTo16x1Float32, RemoveRedundantTranspose, ReplaceMulAddWithDepthwiseConv, @@ -80,6 +81,7 @@ public: RemoveUnnecessaryReshape, TransformMinMaxToRelu6Pass, TransformMinReluToRelu6Pass, + DecomposeHardSwishPass, SubstituteStridedSliceToReshape, SubstituteTransposeToReshape, RemoveRedundantQuantize, diff --git a/compiler/luci/pass/include/luci/CircleQuantizer.h b/compiler/luci/pass/include/luci/CircleQuantizer.h index 4e7074d..463f317 100644 --- a/compiler/luci/pass/include/luci/CircleQuantizer.h +++ b/compiler/luci/pass/include/luci/CircleQuantizer.h @@ -45,6 +45,7 @@ public: CopyQuantParam, ForceQuantParam, ConvertToFakeQuantizedModel, + QuantizeWeights, }; enum AlgorithmParameters diff --git a/compiler/luci/pass/include/luci/DynamicBatchToSingleBatch.h b/compiler/luci/pass/include/luci/DynamicBatchToSingleBatch.h new file mode 100644 index 0000000..2a02777 --- /dev/null +++ b/compiler/luci/pass/include/luci/DynamicBatchToSingleBatch.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_DYNAMIC_BATCH_TO_SINGLE_BATCH_H__ +#define __LUCI_DYNAMIC_BATCH_TO_SINGLE_BATCH_H__ + +#include + +namespace luci +{ + +void dynamic_batch_to_single_batch(luci::Module *); + +} // namespace luci + +#endif // __LUCI_DYNAMIC_BATCH_TO_SINGLE_BATCH_H__ diff --git a/onert-micro/luci-interpreter/src/loader/nodes/If.cpp b/compiler/luci/pass/include/luci/Pass/DecomposeHardSwishPass.h similarity index 53% rename from onert-micro/luci-interpreter/src/loader/nodes/If.cpp rename to compiler/luci/pass/include/luci/Pass/DecomposeHardSwishPass.h index 1c6f12c..83c16bc 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/If.cpp +++ b/compiler/luci/pass/include/luci/Pass/DecomposeHardSwishPass.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,24 @@ * limitations under the License. */ -#include "Builders.h" +#ifndef __LUCI_DECOMPOSE_HARDSWISH_PASS_H__ +#define __LUCI_DECOMPOSE_HARDSWISH_PASS_H__ -#include "kernels/If.h" +#include -namespace luci_interpreter +namespace luci { -std::unique_ptr build_kernel_CircleIf(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) +/** + * @brief Class to decompose HardSwish to Add, Mul and Relu6 + */ +struct DecomposeHardSwishPass final : public logo::Pass { - // TODO: support IF operation - assert(false && "Not supported now"); -} + const char *name(void) const final { return "luci::DecomposeHardSwishPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci -} // namespace luci_interpreter +#endif // __LUCI_DECOMPOSE_HARDSWISH_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/DynamicBatchToSingleBatchPass.h b/compiler/luci/pass/include/luci/Pass/DynamicBatchToSingleBatchPass.h new file mode 100644 index 0000000..b3598c9 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/DynamicBatchToSingleBatchPass.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_DYNAMIC_BATCH_TO_SINGLE_BATCH_PASS_H__ +#define __LUCI_DYNAMIC_BATCH_TO_SINGLE_BATCH_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Pass to convert dynamic batch to single batch + */ +class DynamicBatchToSingleBatchPass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "luci::DynamicBatchToSingleBatchPass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace luci + +#endif //__LUCI_DYNAMIC_BATCH_TO_SINGLE_BATCH_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FuseGeluPass.h b/compiler/luci/pass/include/luci/Pass/FuseGeluPass.h new file mode 100644 index 0000000..5fa2303 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FuseGeluPass.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GELU_PASS_H__ +#define __LUCI_FUSE_GELU_PASS_H__ + +#include + +namespace luci +{ + +/** + * @brief Class to fuse certain pattern of subgraph into CircleGelu + * + * For detailed subgraph pattern to be fused, please check its implementation. + */ +struct FuseGeluPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FuseGeluPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FUSE_GELU_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/QuantizeWeightsPass.h b/compiler/luci/pass/include/luci/Pass/QuantizeWeightsPass.h new file mode 100644 index 0000000..6465973 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/QuantizeWeightsPass.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_QUANTIZE_WEIGHTS_PASS_H__ +#define __LUCI_QUANTIZE_WEIGHTS_PASS_H__ + +#include + +#include + +#include + +namespace luci +{ + +/** + * @brief Pass to quantize weights + */ +class QuantizeWeightsPass : public logo::Pass +{ +public: + struct Context + { + loco::DataType input_model_dtype = loco::DataType::Unknown; + loco::DataType output_model_dtype = loco::DataType::Unknown; + QuantizationGranularity granularity = QuantizationGranularity::ChannelWise; + }; + +public: + QuantizeWeightsPass(std::unique_ptr &&ctx) : _ctx{std::move(ctx)} + { + // DO NOTHING + } + +public: + QuantizeWeightsPass(loco::DataType input_model_dtype, loco::DataType output_model_dtype, + QuantizationGranularity granularity) + { + _ctx = std::make_unique(); + { + _ctx->input_model_dtype = input_model_dtype; + _ctx->output_model_dtype = output_model_dtype; + _ctx->granularity = granularity; + } + } + virtual const char *name(void) const { return "luci::QuantizeWeightsPass"; } + +public: + bool run(loco::Graph *graph); + +private: + std::unique_ptr _ctx; +}; + +} // namespace luci + +#endif //__LUCI_QUANTIZE_WEIGHTS_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RequantizePass.h b/compiler/luci/pass/include/luci/Pass/RequantizePass.h index c6c424f..50b9073 100644 --- a/compiler/luci/pass/include/luci/Pass/RequantizePass.h +++ b/compiler/luci/pass/include/luci/Pass/RequantizePass.h @@ -27,7 +27,7 @@ namespace luci { /** - * @brief Pass to quantize weights + * @brief Pass to re-quantize graph (ex: int8 -> uint8) */ class RequantizePass : public logo::Pass { diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp index 5e1613a..b011581 100644 --- a/compiler/luci/pass/src/CircleOptimizer.cpp +++ b/compiler/luci/pass/src/CircleOptimizer.cpp @@ -39,6 +39,7 @@ #include "luci/Pass/FuseMeanWithMeanPass.h" #include "luci/Pass/FusePreActivationBatchNormPass.h" #include "luci/Pass/FusePReluPass.h" +#include "luci/Pass/FuseGeluPass.h" #include "luci/Pass/FuseTransposeWithMeanPass.h" #include "luci/Pass/MakeBatchNormGammaPositivePass.h" #include "luci/Pass/RemoveDuplicateConstPass.h" @@ -70,6 +71,7 @@ #include "luci/Pass/SubstituteTransposeToReshapePass.h" #include "luci/Pass/TransformMinMaxToRelu6Pass.h" #include "luci/Pass/TransformMinReluToRelu6Pass.h" +#include "luci/Pass/DecomposeHardSwishPass.h" #include "luci/Pass/UnrollUnidirectionalSequenceLSTMPass.h" // TODO add more passes @@ -137,7 +139,8 @@ bool OptimizeOptionsImpl::query(Algorithm algo) } // TODO Make a struct for args -void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_output, bool fuse_fc) +void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_output, bool fuse_fc, + bool fuse_gelu) { logo::Phase phase; @@ -160,6 +163,12 @@ void convert_nchw_to_nhwc(loco::Graph *g, bool preserve_input, bool preserve_out if (fuse_fc) phase.emplace_back(std::make_unique()); + // Fuse decomposed ops to Gelu Op + // Why here? ConverNCHWToNHWCPass inserts additional Ops, so it is better to fuse + // Gelu in advance. + if (fuse_gelu) + phase.emplace_back(std::make_unique()); + phase.emplace_back( std::make_unique(preserve_input, preserve_output)); @@ -216,8 +225,9 @@ void CircleOptimizer::optimize(loco::Graph *g) const _options->param(Options::AlgorithmParameters::NCHW_to_NHWC_output_shape) != "true"; bool fuse_fc = _options->query(Options::Algorithm::FuseAddWithFullyConnected); + bool fuse_gelu = _options->query(Options::Algorithm::FuseGelu); - convert_nchw_to_nhwc(g, preserve_input, preserve_output, fuse_fc); + convert_nchw_to_nhwc(g, preserve_input, preserve_output, fuse_fc, fuse_gelu); } /* TRANSFORM DECLARATION BEGIN */ @@ -283,6 +293,10 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } + if (_options->query(Options::Algorithm::FuseGelu)) + { + phase.emplace_back(std::make_unique()); + } if (_options->query(Options::Algorithm::FuseTransposeWithMean)) { phase.emplace_back(std::make_unique()); @@ -319,14 +333,6 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } - if (_options->query(Options::Algorithm::ForwardReshapeToUnaryOp)) - { - phase.emplace_back(std::make_unique()); - } - if (_options->query(Options::Algorithm::ForwardTransposeOp)) - { - phase.emplace_back(std::make_unique()); - } if (_options->query(Options::Algorithm::FusePreActivationBatchNorm)) { phase.emplace_back(std::make_unique()); @@ -428,10 +434,26 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique()); } + if (_options->query(Options::Algorithm::DecomposeHardSwishPass)) + { + phase.emplace_back(std::make_unique()); + } if (_options->query(Options::Algorithm::UnrollUnidirSeqLSTM)) { phase.emplace_back(std::make_unique()); } + // Forward Reshape/Transpose is done after + // 1. SubstituteXXXToReshape + // 2. RemoveRedundantReshape/Transpose + // See https://github.com/Samsung/ONE/pull/10596 for more details + if (_options->query(Options::Algorithm::ForwardReshapeToUnaryOp)) + { + phase.emplace_back(std::make_unique()); + } + if (_options->query(Options::Algorithm::ForwardTransposeOp)) + { + phase.emplace_back(std::make_unique()); + } /* TRANSFORM DECLARATION END */ diff --git a/compiler/luci/pass/src/CircleQuantizer.cpp b/compiler/luci/pass/src/CircleQuantizer.cpp index 3ffa118..9039a83 100644 --- a/compiler/luci/pass/src/CircleQuantizer.cpp +++ b/compiler/luci/pass/src/CircleQuantizer.cpp @@ -26,6 +26,7 @@ #include "luci/Pass/QuantizePreCheckerPass.h" #include "luci/Pass/QuantizeWithMinMaxPass.h" #include "luci/Pass/QuantizeDequantizeWeightsPass.h" +#include "luci/Pass/QuantizeWeightsPass.h" #include "luci/Pass/CircleShapeInferencePass.h" #include "luci/Pass/CircleTypeInferencePass.h" @@ -439,14 +440,14 @@ void CircleQuantizer::quantize(loco::Graph *g) const throw std::runtime_error("Unsupported granularity. List of supported granularity: " + to_string(qwmm_supported_granularity)); - for (auto dtype : input_type_vec) + for (const auto &dtype : input_type_vec) { if (!in_array(to_lower_case(dtype), qwmm_supported_input_type)) throw std::runtime_error("Unsupported input type. List of supported input types: " + to_string(qwmm_supported_input_type)); } - for (auto dtype : output_type_vec) + for (const auto &dtype : output_type_vec) { if (!in_array(to_lower_case(dtype), qwmm_supported_output_type)) throw std::runtime_error("Unsupported output type. List of supported output types: " + @@ -536,6 +537,40 @@ void CircleQuantizer::quantize(loco::Graph *g) const verifier.verify(g); } + if (_options->query(Options::Algorithm::QuantizeWeights)) + { + static const std::vector qw_supported_input_model_dtype{"float32"}; + static const std::vector qw_supported_output_model_dtype{"int8", "int16"}; + static const std::vector qw_supported_granularity{"channel"}; + + auto input_model_dtype = + _options->param(Options::AlgorithmParameters::Quantize_input_model_dtype); + auto output_model_dtype = + _options->param(Options::AlgorithmParameters::Quantize_output_model_dtype); + auto granularity = _options->param(Options::AlgorithmParameters::Quantize_granularity); + + if (!in_array(to_lower_case(input_model_dtype), qw_supported_input_model_dtype)) + throw std::runtime_error("Unsupported input type. List of supported input type: " + + to_string(qw_supported_input_model_dtype)); + + if (!in_array(to_lower_case(output_model_dtype), qw_supported_output_model_dtype)) + throw std::runtime_error("Unsupported output type. List of supported output type: " + + to_string(qw_supported_output_model_dtype)); + + if (!in_array(to_lower_case(granularity), qw_supported_granularity)) + throw std::runtime_error("Unsupported granularity. List of supported granularity: " + + to_string(qw_supported_granularity)); + auto ctx = std::make_unique(); + { + ctx->input_model_dtype = str_to_dtype(input_model_dtype); + ctx->output_model_dtype = str_to_dtype(output_model_dtype); + ctx->granularity = str_to_granularity(granularity); + } + luci::QuantizeWeightsPass weights_quantizer(std::move(ctx)); + + weights_quantizer.run(g); + } + // Requantize if (_options->query(Options::Algorithm::Requantize)) { diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp index 99e1e29..ac43202 100644 --- a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp @@ -55,16 +55,18 @@ bool broadcastable(const luci::CircleConst *from, const luci::CircleNode *to) return true; } -// Expand node to rank 4 +// Return node with rank 4 // node should have rank less than or equal to 4 -void expand_to_rank_4(luci::CircleConst *node) +// 1 is inserted to the front of shape if rank is less than 4 +// For example, [2] -> [1, 1, 1, 2] +luci::CircleConst *expand_to_rank_4(luci::CircleConst *node) { auto original_rank = node->rank(); assert(original_rank <= 4); // FIX_CALLER_UNLESS if (original_rank == 4) - return; + return node; std::vector original_shape; for (uint32_t i = 0; i < original_rank; i++) @@ -72,12 +74,17 @@ void expand_to_rank_4(luci::CircleConst *node) original_shape.emplace_back(node->dim(i).value()); } - node->rank(4); + auto cloned = luci::clone(node); + cloned->name(cloned->name() + "_rank4"); + + cloned->rank(4); for (uint32_t i = 0; i < (4 - original_rank); i++) - node->dim(i) = 1; + cloned->dim(i) = 1; for (uint32_t i = 0; i < original_rank; i++) - node->dim(i + (4 - original_rank)) = original_shape.at(i); + cloned->dim(i + (4 - original_rank)) = original_shape.at(i); + + return cloned; } bool is_output(const loco::Node *node) @@ -564,7 +571,7 @@ bool is_NCHW_with_const(const luci::CircleMul *node, luci::CircleNode *&pred_nod if (not broadcastable(multiplier, node)) return false; - expand_to_rank_4(multiplier); + multiplier = expand_to_rank_4(multiplier); return true; } @@ -602,7 +609,7 @@ bool is_NCHW_with_const(const luci::CircleAdd *node, luci::CircleNode *&pred_nod if (not broadcastable(beta, node)) return false; - expand_to_rank_4(beta); + beta = expand_to_rank_4(beta); return true; } @@ -834,6 +841,8 @@ class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor bool visit(luci::CircleElu *node) { return convert_unary_features(node); } + bool visit(luci::CircleGelu *node) { return convert_unary_features(node); } + bool visit(luci::CircleLeakyRelu *node) { return convert_unary_features(node); @@ -1510,6 +1519,7 @@ bool ConvertNCHWToNHWCPass::run(loco::Graph *g) case luci::CircleOpcode::ADD: case luci::CircleOpcode::CONCATENATION: case luci::CircleOpcode::ELU: + case luci::CircleOpcode::GELU: case luci::CircleOpcode::LEAKY_RELU: case luci::CircleOpcode::LOGISTIC: case luci::CircleOpcode::MAXIMUM: diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp index fd32651..85648cf 100644 --- a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp @@ -535,6 +535,8 @@ public: luci::CircleMaximum *max = nullptr; }; +static constexpr std::initializer_list kDefaultShape = {1, 16, 1, 1}; + class MeanGraph final : public SimpleGraph { protected: @@ -577,7 +579,7 @@ public: private: bool _keep_dims = true; std::vector _axes = {2, 3}; - std::initializer_list _shape = {1, 16, 1, 1}; + std::initializer_list _shape = kDefaultShape; }; class MinimumGraph final : public SimpleGraph @@ -876,7 +878,7 @@ public: private: bool _keep_dims = true; std::vector _axes = {2, 3}; - std::initializer_list _shape = {1, 16, 1, 1}; + std::initializer_list _shape = kDefaultShape; }; class ReduceMinGraph final : public SimpleGraph @@ -921,7 +923,7 @@ public: private: bool _keep_dims = true; std::vector _axes = {2, 3}; - std::initializer_list _shape = {1, 16, 1, 1}; + std::initializer_list _shape = kDefaultShape; }; class ReluGraph final : public SimpleGraph diff --git a/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp b/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp index aacfce3..ae5ab15 100644 --- a/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp +++ b/compiler/luci/pass/src/ConvertToFakeQuantizedModelPass.cpp @@ -198,6 +198,7 @@ struct FakeQuantize final : public luci::CircleNodeMutableVisitor void visit(luci::CircleDepthwiseConv2D *node) { fq_activation(node); } void visit(luci::CircleDiv *node) { fq_activation(node); } void visit(luci::CircleFullyConnected *node) { fq_activation(node); } + void visit(luci::CircleGelu *node) { fq_activation(node); } void visit(luci::CircleInstanceNorm *node) { fq_activation(node); } void visit(luci::CircleLeakyRelu *node) { fq_activation(node); } void visit(luci::CircleLogistic *node) { fq_activation(node); } @@ -217,6 +218,9 @@ struct FakeQuantize final : public luci::CircleNodeMutableVisitor void visit(luci::CircleRsqrt *node) { fq_activation(node); } void visit(luci::CircleSoftmax *node) { fq_activation(node); } void visit(luci::CircleSqrt *node) { fq_activation(node); } + void visit(luci::CircleSquaredDifference *node) { fq_activation(node); } + void visit(luci::CircleSub *node) { fq_activation(node); } + void visit(luci::CircleSum *node) { fq_activation(node); } void visit(luci::CircleTanh *node) { fq_activation(node); } void visit(luci::CircleTransposeConv *node) { fq_activation(node); } diff --git a/compiler/luci/pass/src/DecomposeHardSwishPass.cpp b/compiler/luci/pass/src/DecomposeHardSwishPass.cpp new file mode 100644 index 0000000..bd99d2d --- /dev/null +++ b/compiler/luci/pass/src/DecomposeHardSwishPass.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/DecomposeHardSwishPass.h" + +#include "helpers/NodeFiller.h" +#include "helpers/TypeMapper.h" + +#include +#include + +namespace +{ +/** + * BEFORE + * [CircleNode] + * | + * | + * [CircleHardSwish] + * | + * | + * [CircleNode] + * + * + * AFTER + * + * [CircleNode] [CircleConst] + * | \ / + * | \ / + * | [CircleAdd] + * | | + * | | + * \ [CircleRelu6] [CircleConst] + * \ \ / + * \ \ / + * \ [CircleMul] + * \ / + * \ / + * [CircleMul] + * | + * | + * [CircleNode] + * + */ +bool decompose_hardswish(luci::CircleHardSwish *hardswish) +{ + if (not hardswish) + return false; + + if (hardswish->dtype() != loco::DataType::FLOAT32) + return false; + + auto g = hardswish->graph(); + + auto name = hardswish->name(); + assert(name.length() > 0); + + // Create a const for CircleAdd operation + auto add_const = g->nodes()->create(); + add_const->shape({}); // scalar + add_const->dtype(loco::DataType::FLOAT32); + add_const->rank(0); + add_const->size(1); + add_const->at(0) = 3.; + add_const->name(name + "/Add/const"); + luci::add_origin(add_const, luci::get_origin(hardswish)); + + // Create an Add operation + auto add = g->nodes()->create(); + add->fusedActivationFunction(luci::FusedActFunc::NONE); + add->x(hardswish->features()); + add->y(add_const); + add->name(name + "/Add"); + luci::add_origin(add, luci::get_origin(hardswish)); + + // Create a Relu6 operation + auto relu6 = g->nodes()->create(); + relu6->features(add); + relu6->name(name + "/Relu6"); + luci::add_origin(relu6, luci::get_origin(hardswish)); + + // Create a const for CircleMul operation + auto mul_const = g->nodes()->create(); + mul_const->shape({}); // scalar + mul_const->dtype(loco::DataType::FLOAT32); + mul_const->rank(0); + mul_const->size(1); + mul_const->at(0) = 1. / 6.; + mul_const->name(name + "/Mul/const"); + luci::add_origin(mul_const, luci::get_origin(hardswish)); + + // Create first Mul operation + auto mul1 = g->nodes()->create(); + mul1->fusedActivationFunction(luci::FusedActFunc::NONE); + mul1->x(relu6); + mul1->y(mul_const); + mul1->name(name + "/Mul1"); + luci::add_origin(mul1, luci::get_origin(hardswish)); + + // Create second Mul operation + auto mul2 = g->nodes()->create(); + mul2->fusedActivationFunction(luci::FusedActFunc::NONE); + mul2->x(hardswish->features()); + mul2->y(mul1); + mul2->name(name + "/Mul2"); + luci::add_origin(mul2, luci::get_origin(hardswish)); + + replace(hardswish).with(mul2); + + return true; +} + +} // namespace + +namespace luci +{ + +bool DecomposeHardSwishPass::run(loco::Graph *g) +{ + bool changed = false; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto hardswish = dynamic_cast(node)) + { + if (decompose_hardswish(hardswish)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/DecomposeHardSwishPass.test.cpp b/compiler/luci/pass/src/DecomposeHardSwishPass.test.cpp new file mode 100644 index 0000000..d51a07f --- /dev/null +++ b/compiler/luci/pass/src/DecomposeHardSwishPass.test.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/DecomposeHardSwishPass.h" + +#include + +#include + +namespace +{ + +/** + * HardSwish graph + * + * [CircleInput] + * | + * | + * [CircleHardSwish] + * | + * | + * [CircleOutput] + */ +struct HardSwishGraph +{ + loco::Graph _g; + luci::CircleInput *_input = nullptr; + luci::CircleHardSwish *_hardswish = nullptr; + luci::CircleOutput *_output = nullptr; +}; + +class DecomposeHardSwishPass : public ::testing::Test +{ +protected: + void MakeGraph() + { + const int N = 1; + const int H = 4; + const int W = 4; + const int C = 3; + + // graph input and output + auto graph_input = _hardswish_g._g.inputs()->create(); + auto graph_output = _hardswish_g._g.outputs()->create(); + + // CircleInput + _hardswish_g._input = _hardswish_g._g.nodes()->create(); + _hardswish_g._input->index(graph_input->index()); + _hardswish_g._input->shape({N, H, W, C}); + _hardswish_g._input->dtype(loco::DataType::FLOAT32); + _hardswish_g._input->name("input"); + + // CircleHardSwish + _hardswish_g._hardswish = _hardswish_g._g.nodes()->create(); + _hardswish_g._hardswish->features(_hardswish_g._input); + _hardswish_g._hardswish->shape({N, H, W, C}); + _hardswish_g._hardswish->dtype(loco::DataType::FLOAT32); + _hardswish_g._hardswish->name("hardswish"); + + // CircleOutput + _hardswish_g._output = _hardswish_g._g.nodes()->create(); + _hardswish_g._output->index(graph_output->index()); + _hardswish_g._output->from(_hardswish_g._hardswish); + _hardswish_g._output->shape({N, H, W, C}); + _hardswish_g._output->dtype(loco::DataType::FLOAT32); + _hardswish_g._output->name("output"); + } + + void MakeInt32Graph() + { + const int N = 1; + const int H = 4; + const int W = 4; + const int C = 3; + + // graph input and output + auto graph_input = _hardswish_int32_g._g.inputs()->create(); + auto graph_output = _hardswish_int32_g._g.outputs()->create(); + + // CircleInput + _hardswish_int32_g._input = _hardswish_int32_g._g.nodes()->create(); + _hardswish_int32_g._input->index(graph_input->index()); + _hardswish_int32_g._input->shape({N, H, W, C}); + _hardswish_int32_g._input->dtype(loco::DataType::S32); + _hardswish_int32_g._input->name("input"); + + // CircleHardSwish + _hardswish_int32_g._hardswish = _hardswish_int32_g._g.nodes()->create(); + _hardswish_int32_g._hardswish->features(_hardswish_int32_g._input); + _hardswish_int32_g._hardswish->shape({N, H, W, C}); + _hardswish_int32_g._hardswish->dtype(loco::DataType::S32); + _hardswish_int32_g._hardswish->name("hardswish"); + + // CircleOutput + _hardswish_int32_g._output = _hardswish_int32_g._g.nodes()->create(); + _hardswish_int32_g._output->index(graph_output->index()); + _hardswish_int32_g._output->from(_hardswish_int32_g._hardswish); + _hardswish_int32_g._output->shape({N, H, W, C}); + _hardswish_int32_g._output->dtype(loco::DataType::S32); + _hardswish_int32_g._output->name("output"); + } + + virtual void SetUp() + { + MakeGraph(); + MakeInt32Graph(); + } + +protected: + luci::DecomposeHardSwishPass _pass; + HardSwishGraph _hardswish_g; + HardSwishGraph _hardswish_int32_g; +}; + +} // namespace + +TEST_F(DecomposeHardSwishPass, name) +{ + auto const name = _pass.name(); + ASSERT_NE(nullptr, name); +} + +/** + * Decomposed graph looks like below. + * + * [CircleInput] [CircleConst] + * | \ / + * | \ / + * | [CircleAdd] + * | | + * | | + * \ [CircleRelu6] [CircleConst] + * \ \ / + * \ \ / + * \ [CircleMul] + * \ / + * \ / + * [CircleMul] + * | + * | + * [CircleOutput] + * + */ +TEST_F(DecomposeHardSwishPass, simple_test) +{ + auto ret = _pass.run(&_hardswish_g._g); + EXPECT_TRUE(ret); + + auto mul2 = dynamic_cast(_hardswish_g._output->from()); + EXPECT_NE(nullptr, mul2); + + auto input2 = dynamic_cast(mul2->x()); + EXPECT_NE(nullptr, input2); + + auto mul1 = dynamic_cast(mul2->y()); + EXPECT_NE(nullptr, mul1); + + auto relu6 = dynamic_cast(mul1->x()); + EXPECT_NE(nullptr, relu6); + + auto mul_const = dynamic_cast(mul1->y()); + EXPECT_NE(nullptr, mul_const); + EXPECT_FLOAT_EQ(1. / 6., mul_const->at(0)); + + auto add = dynamic_cast(relu6->features()); + EXPECT_NE(nullptr, add); + + auto input1 = dynamic_cast(add->x()); + EXPECT_NE(nullptr, input1); + + auto add_const = dynamic_cast(add->y()); + EXPECT_NE(nullptr, add_const); + EXPECT_FLOAT_EQ(3., add_const->at(0)); +} + +TEST_F(DecomposeHardSwishPass, check_last_node) +{ + auto ret = _pass.run(&_hardswish_g._g); + EXPECT_TRUE(ret); + + auto hardswish = dynamic_cast(_hardswish_g._output->from()); + EXPECT_EQ(nullptr, hardswish); +} + +TEST_F(DecomposeHardSwishPass, wrong_condition_NEG) +{ + auto ret = _pass.run(&_hardswish_int32_g._g); + EXPECT_FALSE(ret); + + auto hardswish = dynamic_cast(_hardswish_g._output->from()); + EXPECT_NE(nullptr, hardswish); +} diff --git a/compiler/luci/pass/src/DynamicBatchToSingleBatch.cpp b/compiler/luci/pass/src/DynamicBatchToSingleBatch.cpp new file mode 100644 index 0000000..8687606 --- /dev/null +++ b/compiler/luci/pass/src/DynamicBatchToSingleBatch.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/DynamicBatchToSingleBatch.h" + +#include "luci/Pass/DynamicBatchToSingleBatchPass.h" +#include "luci/Pass/CircleShapeInferencePass.h" + +#include "ProgressReporter.h" + +#include + +namespace luci +{ + +void dynamic_batch_to_single_batch(luci::Module *m) +{ + assert(m); // FIX CALLER UNLESS + + for (uint32_t i = 0; i < m->size(); i++) + { + auto g = m->graph(i); + + logo::Phase phase; + + phase.emplace_back(std::make_unique()); + + // Needed to infer shapes of other nodes + phase.emplace_back(std::make_unique()); + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); + } +} + +} // namespace luci diff --git a/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.cpp b/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.cpp new file mode 100644 index 0000000..59a9f5a --- /dev/null +++ b/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/DynamicBatchToSingleBatchPass.h" + +#include +#include + +namespace luci +{ + +bool DynamicBatchToSingleBatchPass::run(loco::Graph *g) +{ + assert(g); // FIX CALLER UNLESS + + bool changed = false; + + auto graph_inputs = g->inputs(); + + // Assume the first dimension is batch dimension + const uint32_t BATCH_DIM = 0; + + for (auto node : loco::input_nodes(g)) + { + auto input_node = loco::must_cast(node); + + if (input_node->rank() == 0) + continue; + + // Skip if batch dimension is known + if (input_node->dim(BATCH_DIM).known()) + continue; + + if (input_node->rank() != 4) + { + // Limit use only for rank 4 inputs (for NHWC and NCHW) + // TODO Enable this if necessary + throw std::runtime_error("First dimension of input is unknown, but its rank is not 4."); + } + + // 'set' will make the dimension known + input_node->dim(BATCH_DIM).set(1); + + // Update graph input + auto graph_input = graph_inputs->at(input_node->index()); + auto graph_input_shape = graph_input->shape(); + auto tensor_shape = std::make_unique(); + { + tensor_shape->rank(graph_input_shape->rank()); + for (uint32_t i = 0; i < tensor_shape->rank(); i++) + { + tensor_shape->dim(i) = graph_input_shape->dim(i); + } + tensor_shape->dim(BATCH_DIM).set(1); + } + + graph_input->shape(std::move(tensor_shape)); + + changed = true; + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.test.cpp b/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.test.cpp new file mode 100644 index 0000000..f19f57d --- /dev/null +++ b/compiler/luci/pass/src/DynamicBatchToSingleBatchPass.test.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/DynamicBatchToSingleBatchPass.h" + +#include + +#include + +#include + +namespace +{ + +std::unique_ptr make_tshape(std::initializer_list dims) +{ + auto tensor_shape = std::make_unique(); + { + tensor_shape->rank(dims.size()); + uint32_t axis = 0; + for (auto it = dims.begin(); it != dims.end(); ++it) + { + tensor_shape->dim(axis++) = *it; + } + } + + return std::move(tensor_shape); +} + +} // namespace + +TEST(DynamicBatchToSingleBatchPassTest, simple) +{ + luci::DynamicBatchToSingleBatchPass pass; + + auto g = loco::make_graph(); + + auto graph_input = g->inputs()->create(); + { + auto tensor_shape = make_tshape({1, 5, 5, 3}); + tensor_shape->dim(0).unset(); + graph_input->shape(std::move(tensor_shape)); + } + + // Create nodes to make relu traversed first + auto input = g->nodes()->create(); + { + input->index(0); + input->shape({1, 5, 5, 3}); + input->dim(0).unset(); + } + + EXPECT_FALSE(graph_input->shape()->dim(0).known()); + EXPECT_FALSE(input->dim(0).known()); + + EXPECT_TRUE(pass.run(g.get())); + + // Check input is knwon + EXPECT_TRUE(graph_input->shape()->dim(0).known()); + EXPECT_EQ(1, graph_input->shape()->dim(0)); + EXPECT_TRUE(input->dim(0).known()); + EXPECT_EQ(1, input->dim(0)); +} + +TEST(DynamicBatchToSingleBatchPassTest, simple_NEG) +{ + luci::DynamicBatchToSingleBatchPass pass; + + auto g = loco::make_graph(); + + auto graph_input = g->inputs()->create(); + { + graph_input->shape({1, 5, 5, 3}); + } + + // Create nodes to make relu traversed first + auto input = g->nodes()->create(); + { + input->index(0); + input->shape({1, 5, 5, 3}); + } + + EXPECT_FALSE(pass.run(g.get())); +} + +// Remove this test if we support rank 1 in this pass +TEST(DynamicBatchToSingleBatchPassTest, rank1_NEG) +{ + luci::DynamicBatchToSingleBatchPass pass; + + auto g = loco::make_graph(); + + auto graph_input = g->inputs()->create(); + { + auto tensor_shape = make_tshape({1}); + tensor_shape->dim(0).unset(); + graph_input->shape(std::move(tensor_shape)); + } + + // Create nodes to make relu traversed first + auto input = g->nodes()->create(); + { + input->index(0); + input->shape({1}); + input->dim(0).unset(); + } + + EXPECT_FALSE(graph_input->shape()->dim(0).known()); + EXPECT_FALSE(input->dim(0).known()); + + // Rank 1 is unsupported for now + EXPECT_ANY_THROW(pass.run(g.get())); +} diff --git a/compiler/luci/pass/src/FoldAddV2Pass.test.cpp b/compiler/luci/pass/src/FoldAddV2Pass.test.cpp index 438d7f0..200fcc0 100644 --- a/compiler/luci/pass/src/FoldAddV2Pass.test.cpp +++ b/compiler/luci/pass/src/FoldAddV2Pass.test.cpp @@ -44,10 +44,10 @@ template class FoldAddV2Test : public luci::ConstantFoldingAd public: FoldAddV2Test(std::initializer_list shape) : luci::ConstantFoldingAddTestGraph(shape, T) { - _addV2 = _g.nodes()->create(2, 1); - _x = _g.nodes()->create(); - _y = _g.nodes()->create(); - _addV2_out = _g.nodes()->create(); + _addV2 = _g.nodes()->template create(2, 1); + _x = _g.nodes()->template create(); + _y = _g.nodes()->template create(); + _addV2_out = _g.nodes()->template create(); _addV2->dtype(T); _x->dtype(T); diff --git a/compiler/luci/pass/src/FoldCastPass.test.cpp b/compiler/luci/pass/src/FoldCastPass.test.cpp index 5911adf..da33e43 100644 --- a/compiler/luci/pass/src/FoldCastPass.test.cpp +++ b/compiler/luci/pass/src/FoldCastPass.test.cpp @@ -31,8 +31,8 @@ public: FoldCastTest(std::initializer_list shape) : luci::ConstantFoldingAddTestGraph(shape, ToT) { - _cast = _g.nodes()->create(); - _x = _g.nodes()->create(); + _cast = _g.nodes()->template create(); + _x = _g.nodes()->template create(); _cast->dtype(ToT); _x->dtype(FromT); diff --git a/compiler/luci/pass/src/FoldDequantizePass.test.cpp b/compiler/luci/pass/src/FoldDequantizePass.test.cpp index fb5b6ad..87dff5d 100644 --- a/compiler/luci/pass/src/FoldDequantizePass.test.cpp +++ b/compiler/luci/pass/src/FoldDequantizePass.test.cpp @@ -32,8 +32,8 @@ public: loco::Node *createFoldedPattern() override { - _dequantize = _g.nodes()->create(); - _input = _g.nodes()->create(); + _dequantize = _g.nodes()->template create(); + _input = _g.nodes()->template create(); _dequantize->dtype(loco::DataType::FLOAT32); _input->dtype(DT); diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp index d83973c..868ccd1 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp +++ b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp @@ -42,6 +42,11 @@ bool fuse_activation_function(luci::CircleNode *node) // This will skip fuse for concat as luci-interpreter doesn't support this yet if (dynamic_cast(pred_node) != nullptr) return false; + // TODO remove this work-around + // This will skip fuse for TransposeConv as backends does not support this yet + // NOTE remove this when XpSepActFromTransposeConvOpPass is removed + if (dynamic_cast(pred_node) != nullptr) + return false; auto fused_act = node_with_fused_act->fusedActivationFunction(); diff --git a/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.test.cpp b/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.test.cpp index 3007965..b132c6b 100644 --- a/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.test.cpp +++ b/compiler/luci/pass/src/FuseAddWithFullyConnectedPass.test.cpp @@ -16,6 +16,8 @@ #include "luci/Pass/FuseAddWithFullyConnectedPass.h" +#include "helpers/CreateCircleConst.h" + #include #include @@ -27,52 +29,6 @@ namespace using namespace luci::test; -// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp -template -luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, - const std::vector &shape, - const std::vector &values) -{ - auto node = g->nodes()->create(); - node->dtype(dtype); - node->rank(shape.size()); - - uint32_t size = 1; - for (uint32_t i = 0; i < shape.size(); ++i) - { - node->dim(i) = shape.at(i); - size *= shape.at(i); - } - node->shape_status(luci::ShapeStatus::VALID); - -#define INIT_VALUES(DT) \ - { \ - node->size
(size); \ - for (uint32_t i = 0; i < values.size(); ++i) \ - node->at
(i) = values[i]; \ - } - - switch (dtype) - { - case loco::DataType::U8: - INIT_VALUES(loco::DataType::U8); - break; - case loco::DataType::S16: - INIT_VALUES(loco::DataType::S16); - break; - case loco::DataType::S32: - INIT_VALUES(loco::DataType::S32); - break; - case loco::DataType::FLOAT32: - INIT_VALUES(loco::DataType::FLOAT32) - break; - default: - INTERNAL_EXN("create_const_node called with unsupported type"); - break; - } - return node; -} - /** * Simple graph for test * @@ -95,10 +51,10 @@ public: void init(loco::Graph *g) { std::vector weights_val(16 * 4); - _fc_f = create_const_node(g, loco::DataType::FLOAT32, {16, 4}, weights_val); + _fc_f = luci::create_const_node(g, loco::DataType::FLOAT32, {16, 4}, weights_val); std::vector bias_val(16); - _fc_b = create_const_node(g, loco::DataType::FLOAT32, {1, 16}, bias_val); + _fc_b = luci::create_const_node(g, loco::DataType::FLOAT32, {1, 16}, bias_val); _fc = g->nodes()->create(); _fc->weights(_fc_f); @@ -111,7 +67,7 @@ public: std::vector addition_val; for (uint32_t i = 0; i < 16; i++) addition_val.push_back(static_cast(i)); - _add_c = create_const_node(g, loco::DataType::FLOAT32, {1, 16}, addition_val); + _add_c = luci::create_const_node(g, loco::DataType::FLOAT32, {1, 16}, addition_val); _add = g->nodes()->create(); _add->x(_fc); diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp index 852bc8b..d8e9f11 100644 --- a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp +++ b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp @@ -44,6 +44,9 @@ namespace */ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) { + // skip if tconv has fused activation + if (tconv->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; // check whether it has bias or not. This optimization works only if it doesn't. auto bias = dynamic_cast(tconv->bias()); if (not bias) diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp index 265a839..919ce6e 100644 --- a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp +++ b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp @@ -87,6 +87,9 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) return false; if (not luci::fill(&scale, &tconv).with_commutative_args_of(mul)) return false; + // skip if tconv has fused activation + if (tconv->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; // check scale and shift constant attributes // TODO maybe rank check is not needed @@ -215,6 +218,9 @@ bool fused_batch_norm_with_tconv(luci::CircleAdd *add) fused_tconv->stride()->h(tconv->stride()->h()); fused_tconv->stride()->w(tconv->stride()->w()); fused_tconv->name(name + "/TransposeConv"); + // TODO set activation from Add and remove adding following Relu/Relu6 Op + // when all of our backends supports fused activation of TransposeConv + fused_tconv->fusedActivationFunction(luci::FusedActFunc::NONE); luci::add_origin(fused_tconv, luci::composite_origin( {luci::get_origin(add), luci::get_origin(mul), luci::get_origin(tconv)})); diff --git a/compiler/luci/pass/src/FuseGeluPass.cpp b/compiler/luci/pass/src/FuseGeluPass.cpp new file mode 100644 index 0000000..e3e7cec --- /dev/null +++ b/compiler/luci/pass/src/FuseGeluPass.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/FuseGeluPass.h" +#include "helpers/NodeFiller.h" + +#include + +#include +#include + +#include + +#include + +// Helper to fuse Gelu +namespace +{ + +// Float comparison +bool same(float a, float b) { return fabs(a - b) < 1e-5; } + +class GeluPatternBase +{ +public: + GeluPatternBase(luci::CircleMul *candidate) { _pattern_last_node = candidate; } + + virtual ~GeluPatternBase() = default; + +public: + virtual bool matched() = 0; + +public: + luci::CircleNode *_ifm = nullptr; + luci::CircleMul *_mul_sqrt = nullptr; + luci::CircleCustom *_erf = nullptr; + luci::CircleCustomOut *_erf_out = nullptr; + luci::CircleAdd *_add_one = nullptr; + luci::CircleMul *_mul = nullptr; + luci::CircleMul *_mul_half = nullptr; + luci::CircleConst *_const_sqrt = nullptr; + luci::CircleConst *_const_one = nullptr; + luci::CircleConst *_const_half = nullptr; + luci::CircleMul *_pattern_last_node = nullptr; +}; + +/** + * Below diagram shows Gelu pattern to fuse. + * - Gelu(x) = 0.5 * x * (1.0 + erf(x / sqrt(2.0))) + * - the below pattern will be replaced with one Gelu + * + * [In] + * | + * V + * +---- ifm + * | | + * | V + * | mul_sqrt (1/sqrt(2) = 0.707106..) + * | | + * | V + * | erf + * | | + * | V + * | add_one (1.0) + * | | + * | V + * +---> mul + * | + * V + * mul_half (0.5) + * | + * V + * [Out] + * + */ +class GeluPattern1 final : public GeluPatternBase +{ +public: + GeluPattern1(luci::CircleMul *candidate) : GeluPatternBase(candidate) + { + assert(candidate); + _mul_half = candidate; + } + +public: + bool matched() override; +}; + +/** + * Below diagram shows Gelu pattern to fuse. + * - Gelu(x) = 0.5 * x * (1.0 + erf(x / sqrt(2.0))) + * - the below pattern will be replaced with one Gelu + * + * [In] + * | + * V + * +----------- ifm + * | | + * | V + * | mul_sqrt (1/sqrt(2) = 0.707106..) + * | | + * | V + * | erf + * mul_half (0.5) | + * | V + * | add_one (1.0) + * | | + * | V + * +----------> mul + * | + * | + * V + * [Out] + * + */ +class GeluPattern2 final : public GeluPatternBase +{ +public: + GeluPattern2(luci::CircleMul *candidate) : GeluPatternBase(candidate) + { + assert(candidate); + _mul = candidate; + } + + ~GeluPattern2() override = default; + +public: + bool matched() override; +}; + +#define CHECK_OR_FALSE(condition) \ + if (not(condition)) \ + return false; + +bool GeluPattern1::matched() +{ + // check pattern + CHECK_OR_FALSE(luci::fill(&_mul, &_const_half).with_commutative_args_of(_mul_half)); + CHECK_OR_FALSE(luci::fill(&_ifm, &_add_one).with_commutative_args_of(_mul)); + CHECK_OR_FALSE(luci::fill(&_erf_out, &_const_one).with_commutative_args_of(_add_one)); + + if (auto erf = dynamic_cast(_erf_out->input())) + _erf = erf; + + CHECK_OR_FALSE(_erf != nullptr); + + // Check erf + CHECK_OR_FALSE(_erf->custom_code() == "Erf"); + CHECK_OR_FALSE(_erf->numInputs() == 1); + CHECK_OR_FALSE(_erf->numOutputs() == 1); + + if (auto mul_sqrt = dynamic_cast(_erf->inputs(0))) + _mul_sqrt = mul_sqrt; + + CHECK_OR_FALSE(_mul_sqrt != nullptr); + + CHECK_OR_FALSE(luci::fill(&_ifm, &_const_sqrt).with_commutative_args_of(_mul_sqrt)); + + CHECK_OR_FALSE(_mul_sqrt->x() == _ifm); + CHECK_OR_FALSE(_mul->x() == _ifm); + + // Check Activation to be NONE + CHECK_OR_FALSE(_mul_sqrt->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_add_one->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_mul->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_mul_half->fusedActivationFunction() == luci::FusedActFunc::NONE); + + // check _const_sqrt condition + CHECK_OR_FALSE(_const_sqrt->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_sqrt->size() == 1); + CHECK_OR_FALSE(::same(_const_sqrt->at(0), sqrtf(0.5f))); + + // check if _const_half is 0.5 (fp32) + CHECK_OR_FALSE(_const_half->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_half->size() == 1); + CHECK_OR_FALSE(_const_half->at(0) == 0.5); + + // check _const_one condition + CHECK_OR_FALSE(_const_one->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_one->size() == 1); + CHECK_OR_FALSE(_const_one->at(0) == 1); + + return true; +} + +bool GeluPattern2::matched() +{ + // check pattern + CHECK_OR_FALSE(luci::fill(&_mul_half, &_add_one).with_commutative_args_of(_mul)); + CHECK_OR_FALSE(luci::fill(&_ifm, &_const_half).with_commutative_args_of(_mul_half)); + CHECK_OR_FALSE(luci::fill(&_erf_out, &_const_one).with_commutative_args_of(_add_one)); + + CHECK_OR_FALSE(_mul_half->x() == _ifm); + + if (auto erf = dynamic_cast(_erf_out->input())) + _erf = erf; + + CHECK_OR_FALSE(_erf != nullptr); + + // Check erf + CHECK_OR_FALSE(_erf->custom_code() == "Erf"); + CHECK_OR_FALSE(_erf->numInputs() == 1); + CHECK_OR_FALSE(_erf->numOutputs() == 1); + + if (auto mul_sqrt = dynamic_cast(_erf->inputs(0))) + _mul_sqrt = mul_sqrt; + + CHECK_OR_FALSE(_mul_sqrt != nullptr); + + CHECK_OR_FALSE(luci::fill(&_ifm, &_const_sqrt).with_commutative_args_of(_mul_sqrt)); + + CHECK_OR_FALSE(_mul_sqrt->x() == _ifm); + + // Check Activation to be NONE + CHECK_OR_FALSE(_mul_sqrt->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_add_one->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_mul->fusedActivationFunction() == luci::FusedActFunc::NONE); + CHECK_OR_FALSE(_mul_half->fusedActivationFunction() == luci::FusedActFunc::NONE); + + // check _const_sqrt condition + CHECK_OR_FALSE(_const_sqrt->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_sqrt->size() == 1); + CHECK_OR_FALSE(::same(_const_sqrt->at(0), sqrtf(0.5f))); + + // check if _const_half is 0.5 (fp32) + CHECK_OR_FALSE(_const_half->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_half->size() == 1); + CHECK_OR_FALSE(_const_half->at(0) == 0.5); + + // check _const_one condition + CHECK_OR_FALSE(_const_one->dtype() == loco::DataType::FLOAT32); + CHECK_OR_FALSE(_const_one->size() == 1); + CHECK_OR_FALSE(_const_one->at(0) == 1); + + return true; +} + +#undef CHECK_OR_FALSE + +class FuseGelu final +{ +public: + FuseGelu(const GeluPatternBase *p) : _p(p) {} + +public: + void apply(void); + +private: + luci::CircleGelu *create_gelu(loco::Graph *graph); + +private: + const GeluPatternBase *_p; +}; + +luci::CircleGelu *FuseGelu::create_gelu(loco::Graph *graph) +{ + assert(graph); + + auto gelu = graph->nodes()->create(); + gelu->features(_p->_ifm); + // TODO Support approximate = True pattern + gelu->approximate(false); + gelu->name(_p->_pattern_last_node->name() + "_gelu"); + return gelu; +} + +void FuseGelu::apply() +{ + auto graph = _p->_pattern_last_node->graph(); + + auto gelu = create_gelu(graph); + + // set origin + std::vector> origin_vec{ + luci::get_origin(_p->_mul_sqrt), luci::get_origin(_p->_erf), luci::get_origin(_p->_add_one), + luci::get_origin(_p->_mul), luci::get_origin(_p->_mul_half)}; + + luci::add_origin(gelu, luci::composite_origin(origin_vec)); + + replace(_p->_pattern_last_node).with(gelu); +} + +} // namespace + +namespace +{ + +bool fuse_gelu(luci::CircleMul *mul) +{ + assert(mul); + + // check first pattern + GeluPattern1 pattern(mul); + if (pattern.matched()) + { + FuseGelu fuse(&pattern); + fuse.apply(); + return true; + } + + // check second pattern + GeluPattern2 pattern2(mul); + if (pattern2.matched()) + { + FuseGelu fuse(&pattern2); + fuse.apply(); + return true; + } + return false; +} + +} // namespace + +namespace luci +{ + +bool FuseGeluPass::run(loco::Graph *g) +{ + bool changed = false; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto mul = dynamic_cast(node); + if (not mul) + continue; + + if (fuse_gelu(mul)) + changed = true; + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FuseGeluPass.test.cpp b/compiler/luci/pass/src/FuseGeluPass.test.cpp new file mode 100644 index 0000000..db6f699 --- /dev/null +++ b/compiler/luci/pass/src/FuseGeluPass.test.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/FuseGeluPass.h" + +#include + +#include + +#include +#include + +namespace +{ + +using namespace luci::test; + +class GeluGraphlet +{ +public: + GeluGraphlet() = default; + + void init(loco::Graph *g) + { + _ifm = g->nodes()->create(); + _mul_sqrt = g->nodes()->create(); + _erf = g->nodes()->create(1, 1); + _erf_out = g->nodes()->create(); + _add_one = g->nodes()->create(); + _mul = g->nodes()->create(); + _mul_half = g->nodes()->create(); + _const_sqrt = g->nodes()->create(); + _const_one = g->nodes()->create(); + _const_half = g->nodes()->create(); + + _mul->fusedActivationFunction(luci::FusedActFunc::NONE); + _mul_sqrt->fusedActivationFunction(luci::FusedActFunc::NONE); + _mul_half->fusedActivationFunction(luci::FusedActFunc::NONE); + _add_one->fusedActivationFunction(luci::FusedActFunc::NONE); + + _ifm->name("ifm"); + _mul_sqrt->name("mul_sqrt"); + _erf->name("erf"); + _erf_out->name("erf_out"); + _add_one->name("add_one"); + _mul->name("mul"); + _mul_half->name("mul_half"); + _const_one->name("const_one"); + _const_sqrt->name("const_sqrt"); + _const_half->name("const_half"); + + _erf->custom_code("Erf"); + + _const_sqrt->dtype(loco::DataType::FLOAT32); + _const_sqrt->size(1); + _const_sqrt->shape({1}); + _const_sqrt->at(0) = sqrtf(0.5f); + _const_sqrt->shape_status(luci::ShapeStatus::VALID); + + _const_one->dtype(loco::DataType::FLOAT32); + _const_one->size(1); + _const_one->shape({1}); + _const_one->at(0) = 1.0; + _const_one->shape_status(luci::ShapeStatus::VALID); + + _const_half->dtype(loco::DataType::FLOAT32); + _const_half->size(1); + _const_half->shape({1}); + _const_half->at(0) = 0.5; + _const_half->shape_status(luci::ShapeStatus::VALID); + } + + void invalid_half() { _const_half->at(0) = 0.1; } + void invalid_act() { _add_one->fusedActivationFunction(luci::FusedActFunc::RELU); } + +protected: + luci::CircleAbs *_ifm = nullptr; + luci::CircleMul *_mul_sqrt = nullptr; + luci::CircleCustom *_erf = nullptr; + luci::CircleCustomOut *_erf_out = nullptr; + luci::CircleAdd *_add_one = nullptr; + luci::CircleMul *_mul = nullptr; + luci::CircleMul *_mul_half = nullptr; + luci::CircleConst *_const_sqrt = nullptr; + luci::CircleConst *_const_one = nullptr; + luci::CircleConst *_const_half = nullptr; +}; + +class FuseGeluTestGraph1 : public TestIOGraph, public GeluGraphlet +{ +public: + FuseGeluTestGraph1() = default; + + void init(void) + { + TestIOGraph::init({1}, {1}); + GeluGraphlet::init(g()); + + _ifm->x(input()); + _mul_sqrt->x(_ifm); + _mul_sqrt->y(_const_sqrt); + _erf->inputs(0, _mul_sqrt); + _erf_out->input(_erf); + _add_one->x(_erf_out); + _add_one->y(_const_one); + _mul->x(_ifm); + _mul->y(_add_one); + _mul_half->x(_mul); + _mul_half->y(_const_half); + + output()->from(_mul_half); + } +}; + +class FuseGeluTestGraph2 : public TestIOGraph, public GeluGraphlet +{ +public: + FuseGeluTestGraph2() = default; + + void init(void) + { + TestIOGraph::init({1}, {1}); + GeluGraphlet::init(g()); + + _ifm->x(input()); + _mul_sqrt->x(_ifm); + _mul_sqrt->y(_const_sqrt); + _erf->inputs(0, _mul_sqrt); + _erf_out->input(_erf); + _add_one->x(_erf_out); + _add_one->y(_const_one); + _mul_half->x(_ifm); + _mul_half->y(_const_half); + _mul->x(_mul_half); + _mul->y(_add_one); + + output()->from(_mul); + } +}; + +class FuseGeluTestNegGraph : public TestIOGraph, public GeluGraphlet +{ +public: + FuseGeluTestNegGraph() = default; + + void init(void) + { + TestIOGraph::init({1}, {1}); + GeluGraphlet::init(g()); + + _ifm->x(input()); + _mul_sqrt->x(_ifm); + // NOTE y is incorrect (should be _const_sqrt) + _mul_sqrt->y(_ifm); + _erf->inputs(0, _mul_sqrt); + _erf_out->input(_erf); + _add_one->x(_erf_out); + _add_one->y(_const_one); + _mul->x(_ifm); + _mul->y(_add_one); + _mul_half->x(_mul); + _mul_half->y(_const_half); + + output()->from(_mul_half); + } +}; + +} // namespace + +TEST(FuseGeluPassTest, name) +{ + luci::FuseGeluPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(FuseGeluPassTest, fuse_pattern1) +{ + FuseGeluTestGraph1 g; + luci::FuseGeluPass pass; + + g.init(); + + EXPECT_TRUE(pass.run(g.g())); +} + +TEST(FuseGeluPassTest, fuse_pattern2) +{ + FuseGeluTestGraph2 g; + luci::FuseGeluPass pass; + + g.init(); + + EXPECT_TRUE(pass.run(g.g())); +} + +TEST(FuseGeluPassTest, fuse_invalid_half_NEG) +{ + FuseGeluTestNegGraph g; + luci::FuseGeluPass pass; + + g.init(); + g.invalid_half(); + + EXPECT_FALSE(pass.run(g.g())); +} + +TEST(FuseGeluPassTest, fuse_pattern2_invalid_half_NEG) +{ + FuseGeluTestGraph2 g; + luci::FuseGeluPass pass; + + g.init(); + g.invalid_half(); + + EXPECT_FALSE(pass.run(g.g())); +} + +TEST(FuseGeluPassTest, fuse_invalid_act_NEG) +{ + FuseGeluTestNegGraph g; + luci::FuseGeluPass pass; + + g.init(); + g.invalid_act(); + + EXPECT_FALSE(pass.run(g.g())); +} + +TEST(FuseGeluPassTest, fuse_NEG) +{ + FuseGeluTestNegGraph g; + luci::FuseGeluPass pass; + + g.init(); + + EXPECT_FALSE(pass.run(g.g())); +} diff --git a/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp b/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp index e8fa2a4..18617e3b 100644 --- a/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp +++ b/compiler/luci/pass/src/PropagateQParamBackwardPass.cpp @@ -28,6 +28,25 @@ namespace { +// Return true if node is a virtual node +bool virtual_op(const luci::CircleOpcode opcode) +{ + switch (opcode) + { +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + case luci::CircleOpcode::OPCODE: \ + return false; +#define CIRCLE_VNODE(OPCODE, CIRCLE_CLASS) \ + case luci::CircleOpcode::OPCODE: \ + return true; +#include +#undef CIRCLE_NODE +#undef CIRCLE_VNODE + default: + throw std::runtime_error("Unknown opcode detected"); + } +} + void quant_const_values(luci::CircleConst *const_node, float scaling_factor, float zerop, loco::DataType quant_type) { @@ -448,6 +467,50 @@ struct PropagateQParamBackward final : public luci::CircleNodeMutableVisitor(node->tensor()); + + // Do not propagate qparam if input node has multiple users + if (loco::succs(input_node).size() > 1) + return; + + const auto input_opcode = input_node->opcode(); + + // Do not propagate qparam if input node is virtual Op (except CIRCLEINPUT) + // Why? It is not safe to propagate qparam to some virtual nodes. For example, + // const node, multi-out nodes. Let's block them for now. + // TODO Revisit this condition + if (virtual_op(input_opcode) and input_opcode != luci::CircleOpcode::CIRCLEINPUT) + return; + + overwrite_quantparam(node, input_node); + } + + void visit(luci::CircleTranspose *node) + { + auto input_node = loco::must_cast(node->a()); + + // Do not propagate qparam if input node has multiple users + if (loco::succs(input_node).size() > 1) + return; + + const auto input_opcode = input_node->opcode(); + + // Do not propagate qparam if input node is virtual Op (except CIRCLEINPUT) + // Why? It is not safe to propagate qparam to some virtual nodes. For example, + // const node, multi-out nodes. Let's block them for now. + // TODO Revisit this condition + if (virtual_op(input_opcode) and input_opcode != luci::CircleOpcode::CIRCLEINPUT) + return; + + overwrite_quantparam(node, input_node); + } }; } // namespace diff --git a/compiler/luci/pass/src/PropagateQParamBackwardPass.test.cpp b/compiler/luci/pass/src/PropagateQParamBackwardPass.test.cpp index 33af704..04573cc 100644 --- a/compiler/luci/pass/src/PropagateQParamBackwardPass.test.cpp +++ b/compiler/luci/pass/src/PropagateQParamBackwardPass.test.cpp @@ -129,6 +129,119 @@ public: CircleOutput *output = nullptr; }; +/** + * BEFORE + * + * [Input] + * | + * [Conv] (qparam 1) + * | + * [Reshape] (qparam 2) + * | + * [Output] + * + * AFTER + * + * [Input] + * | + * [Conv] (qparam 2) + * | + * [Reshape] (qparam 2) + * | + * [Output] + */ +class ConvReshapeGraph +{ +public: + ConvReshapeGraph() + { + input = g.nodes()->create(); + conv = g.nodes()->create(); + reshape = g.nodes()->create(); + output = g.nodes()->create(); + + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + auto graph_output = g.outputs()->create(); + output->index(graph_output->index()); + + set_qparam(conv, 2.0, 2); + set_qparam(reshape, 1.0, 1); + + conv->input(input); + reshape->tensor(conv); + output->from(reshape); + } + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleConv2D *conv = nullptr; + luci::CircleReshape *reshape = nullptr; + luci::CircleOutput *output = nullptr; +}; + +/** + * BEFORE + * + * [Input] + * | + * [Conv] (qparam 1) + * | + * +---------------------+ + * | | + * [Reshape] (qparam 2) [Output] + * | + * [Output] + * + * AFTER (qparam is not propagated as Conv has multiple users) + * + * [Input] + * | + * [Conv] (qparam 1) + * | + * +---------------------+ + * | | + * [Reshape] (qparam 2) [Output] + * | + * [Output] + */ +class ConvReshapeMultiOutGraph +{ +public: + ConvReshapeMultiOutGraph() + { + input = g.nodes()->create(); + conv = g.nodes()->create(); + reshape = g.nodes()->create(); + output1 = g.nodes()->create(); + output2 = g.nodes()->create(); + + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + auto graph_output1 = g.outputs()->create(); + output1->index(graph_output1->index()); + auto graph_output2 = g.outputs()->create(); + output2->index(graph_output2->index()); + + set_qparam(conv, 2.0, 2); + set_qparam(reshape, 1.0, 1); + + conv->input(input); + reshape->tensor(conv); + output1->from(reshape); + output2->from(conv); + } + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleConv2D *conv = nullptr; + luci::CircleReshape *reshape = nullptr; + luci::CircleOutput *output1 = nullptr; + luci::CircleOutput *output2 = nullptr; +}; + } // namespace TEST(PropagateQParamBackwardPassTest, name) @@ -165,3 +278,33 @@ TEST(PropagateQParamBackwardPassTest, subsequent_propagation) EXPECT_EQ(3.0, graph.input->quantparam()->scale[0]); EXPECT_EQ(3, graph.input->quantparam()->zerop[0]); } + +TEST(PropagateQParamBackwardPassTest, reshape) +{ + ConvReshapeGraph graph; + + EXPECT_NE(graph.conv->quantparam()->scale, graph.reshape->quantparam()->scale); + EXPECT_NE(graph.conv->quantparam()->zerop, graph.reshape->quantparam()->zerop); + + luci::PropagateQParamBackwardPass pass(loco::DataType::U8); + + pass.run(&graph.g); + + EXPECT_EQ(graph.conv->quantparam()->scale, graph.reshape->quantparam()->scale); + EXPECT_EQ(graph.conv->quantparam()->zerop, graph.reshape->quantparam()->zerop); +} + +TEST(PropagateQParamBackwardPassTest, reshape_multi_use_NEG) +{ + ConvReshapeMultiOutGraph graph; + + EXPECT_NE(graph.conv->quantparam()->scale, graph.reshape->quantparam()->scale); + EXPECT_NE(graph.conv->quantparam()->zerop, graph.reshape->quantparam()->zerop); + + luci::PropagateQParamBackwardPass pass(loco::DataType::U8); + + pass.run(&graph.g); + + EXPECT_NE(graph.conv->quantparam()->scale, graph.reshape->quantparam()->scale); + EXPECT_NE(graph.conv->quantparam()->zerop, graph.reshape->quantparam()->zerop); +} diff --git a/compiler/luci/pass/src/QuantizationUtils.cpp b/compiler/luci/pass/src/QuantizationUtils.cpp index 45d229a..3e3cdde 100644 --- a/compiler/luci/pass/src/QuantizationUtils.cpp +++ b/compiler/luci/pass/src/QuantizationUtils.cpp @@ -73,14 +73,14 @@ void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float } void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max, - float &scaling_factor, int64_t &zp, float &nudged_min, + float &scaling_factor, float &nudged_min, float &nudged_max) { const int32_t kMaxScale = std::numeric_limits::max(); const int32_t kMinScale = -kMaxScale; uint32_t size = node->size(); - compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + compute_sym_scale(min, max, scaling_factor, nudged_min, nudged_max); const float scaling_factor_inv = 1.0 / scaling_factor; std::vector quantized_values(size); for (uint32_t i = 0; i < size; ++i) @@ -101,12 +101,14 @@ void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float } } -void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp, - float &nudged_min, float &nudged_max) +void compute_sym_scale(float min, float max, float &scaling_factor, float &nudged_min, + float &nudged_max, loco::DataType out_type) { assert(min <= max); + assert(out_type == loco::DataType::S8 || out_type == loco::DataType::S16); - const int32_t kMaxScale = std::numeric_limits::max(); + const int32_t kMaxScale = (out_type == loco::DataType::S16) ? std::numeric_limits::max() + : std::numeric_limits::max(); const int32_t kMinScale = -kMaxScale; const double qmin_double = kMinScale; const double qmax_double = kMaxScale; @@ -126,10 +128,9 @@ void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t & : scale_factor_from_max_side; // protect scale from being very low to avoid overflow/underflow - if (scaling_factor < 1e-8) - scaling_factor = 1e-8; + const float kMinScalingFactor = (out_type == loco::DataType::S16) ? 1e-8 : 1e-5; + scaling_factor = std::max(scaling_factor, kMinScalingFactor); - zp = 0; nudged_min = static_cast(qmin_double * scaling_factor); nudged_max = static_cast(qmax_double * scaling_factor); } @@ -424,7 +425,7 @@ void quant_const(luci::CircleConst *node, loco::DataType quant_type) nudged_max); break; case loco::DataType::S16: - symmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, zp, nudged_min, + symmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, nudged_min, nudged_max); break; default: diff --git a/compiler/luci/pass/src/QuantizationUtils.h b/compiler/luci/pass/src/QuantizationUtils.h index 0720c98..93c4045 100644 --- a/compiler/luci/pass/src/QuantizationUtils.h +++ b/compiler/luci/pass/src/QuantizationUtils.h @@ -23,9 +23,9 @@ namespace luci { -// Compute scale/zp using given min/max for symmetric quantization (int16) -void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp, - float &nudged_min, float &nudged_max); +// Compute scale using given min/max for symmetric quantization (int8/int16) +void compute_sym_scale(float min, float max, float &scaling_factor, float &nudged_min, + float &nudged_max, loco::DataType out_type = loco::DataType::S16); // Compute scale/zp using given min/max for asymmetric quantization (uint8) void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp, @@ -40,7 +40,7 @@ void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float // Symmetric per-layer quantization of weights (const tensor) using given min/max values // NOTE: in-place update of node data void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max, - float &scaling_factor, int64_t &zp, float &nudged_min, + float &scaling_factor, float &nudged_min, float &nudged_max); // Helper function to get channel dimension diff --git a/compiler/luci/pass/src/QuantizeActivation.cpp b/compiler/luci/pass/src/QuantizeActivation.cpp index 214e61c..9134500 100644 --- a/compiler/luci/pass/src/QuantizeActivation.cpp +++ b/compiler/luci/pass/src/QuantizeActivation.cpp @@ -78,7 +78,7 @@ void QuantizeActivation::visit(luci::CircleNode *node) } else { - compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + compute_sym_scale(min, max, scaling_factor, nudged_min, nudged_max); node->dtype(loco::DataType::S16); } @@ -171,7 +171,10 @@ void QuantizeConstInputActivation::visit(luci::CircleNode *node) auto input_node = node->arg(i); auto const_node = dynamic_cast(input_node); if (const_node != nullptr) - throw std::runtime_error("Unsupported Op for const inputs"); + { + std::string msg = "Unsupported Op for const inputs: " + node->name(); + throw std::runtime_error(msg); + } } } @@ -221,6 +224,7 @@ QUANTIZE_SINGLE_CONST_INPUT(luci::CircleElu, features) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleExp, x) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleFloor, x) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleGather, params) +QUANTIZE_SINGLE_CONST_INPUT(luci::CircleGelu, features) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLocalResponseNormalization, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLogistic, x) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleMean, input) @@ -242,6 +246,7 @@ QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSpaceToDepth, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplit, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplitV, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSqrt, x) +QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSqueeze, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleStridedSlice, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSum, input) QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTanh, x) @@ -256,6 +261,7 @@ QUANTIZE_TWO_CONST_INPUTS(luci::CircleBatchMatMul, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleDiv, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleEqual, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleFloorDiv, x, y) +QUANTIZE_TWO_CONST_INPUTS(luci::CircleFloorMod, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreater, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreaterEqual, x, y) QUANTIZE_TWO_CONST_INPUTS(luci::CircleLess, x, y) diff --git a/compiler/luci/pass/src/QuantizeActivation.h b/compiler/luci/pass/src/QuantizeActivation.h index c6c991a..ba3bc59 100644 --- a/compiler/luci/pass/src/QuantizeActivation.h +++ b/compiler/luci/pass/src/QuantizeActivation.h @@ -111,6 +111,7 @@ private: void visit(luci::CircleExp *node); void visit(luci::CircleFloor *node); void visit(luci::CircleGather *node); + void visit(luci::CircleGelu *node); void visit(luci::CircleLocalResponseNormalization *node); void visit(luci::CircleLogistic *node); void visit(luci::CircleMean *node); @@ -132,6 +133,7 @@ private: void visit(luci::CircleSplit *node); void visit(luci::CircleSplitV *node); void visit(luci::CircleSqrt *node); + void visit(luci::CircleSqueeze *node); void visit(luci::CircleStridedSlice *node); void visit(luci::CircleSum *node); void visit(luci::CircleTanh *node); @@ -146,6 +148,7 @@ private: void visit(luci::CircleDiv *node); void visit(luci::CircleEqual *node); void visit(luci::CircleFloorDiv *node); + void visit(luci::CircleFloorMod *node); void visit(luci::CircleGreater *node); void visit(luci::CircleGreaterEqual *node); void visit(luci::CircleLess *node); diff --git a/compiler/luci/pass/src/QuantizeBias.test.cpp b/compiler/luci/pass/src/QuantizeBias.test.cpp index 0104a19..9030f59 100644 --- a/compiler/luci/pass/src/QuantizeBias.test.cpp +++ b/compiler/luci/pass/src/QuantizeBias.test.cpp @@ -16,6 +16,8 @@ #include "QuantizeBias.h" +#include "helpers/CreateCircleConst.h" + #include #include #include @@ -29,51 +31,6 @@ namespace using namespace luci::test; -// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp -template -luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, - const std::vector &shape, T value) -{ - auto node = g->nodes()->create(); - node->dtype(dtype); - node->rank(shape.size()); - - uint32_t size = 1; - for (uint32_t i = 0; i < shape.size(); ++i) - { - node->dim(i) = shape.at(i); - size *= shape.at(i); - } - node->shape_status(luci::ShapeStatus::VALID); - -#define INIT_VALUES(DT) \ - { \ - node->size
(size); \ - for (uint32_t i = 0; i < size; ++i) \ - node->at
(i) = value; \ - } - - switch (dtype) - { - case loco::DataType::U8: - INIT_VALUES(loco::DataType::U8); - break; - case loco::DataType::S16: - INIT_VALUES(loco::DataType::S16); - break; - case loco::DataType::S32: - INIT_VALUES(loco::DataType::S32); - break; - case loco::DataType::FLOAT32: - INIT_VALUES(loco::DataType::FLOAT32) - break; - default: - INTERNAL_EXN("create_const_node called with unsupported type"); - break; - } - return node; -} - /** * Simple graph for test * diff --git a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp index ef047d3..f8989c9 100644 --- a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp +++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp @@ -110,8 +110,8 @@ void cal_minmax_per_channel(CircleConst *node, std::vector &min, std::vec } void sym_wquant_per_channel(CircleConst *node, std::vector &min, std::vector &max, - std::vector &scaling_factor, std::vector &zp, - std::vector &nudged_min, std::vector &nudged_max) + std::vector &scaling_factor, std::vector &nudged_min, + std::vector &nudged_max) { assert(node->dtype() == loco::DataType::FLOAT32); const int32_t kMaxScale = std::numeric_limits::max(); @@ -122,7 +122,7 @@ void sym_wquant_per_channel(CircleConst *node, std::vector &min, std::vec for (size_t i = 0; i < min.size(); ++i) { - compute_sym_scale_zp(min[i], max[i], scaling_factor[i], zp[i], nudged_min[i], nudged_max[i]); + compute_sym_scale(min[i], max[i], scaling_factor[i], nudged_min[i], nudged_max[i]); } auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { @@ -322,7 +322,7 @@ private: } else { - sym_wquant_per_channel(weights, min, max, scaling_factor, zp, nudged_min, nudged_max); + sym_wquant_per_channel(weights, min, max, scaling_factor, nudged_min, nudged_max); sym_wdequant_per_channel(weights, scaling_factor); } diff --git a/compiler/luci/pass/src/QuantizePreCheckerPass.test.cpp b/compiler/luci/pass/src/QuantizePreCheckerPass.test.cpp index 788353c..8f6a96f 100644 --- a/compiler/luci/pass/src/QuantizePreCheckerPass.test.cpp +++ b/compiler/luci/pass/src/QuantizePreCheckerPass.test.cpp @@ -206,6 +206,7 @@ public: transpose_conv->outBackprop(input_1); transpose_conv->filter(filter); transpose_conv->inputSizes(input_sizes); + transpose_conv->fusedActivationFunction(luci::FusedActFunc::NONE); if (make_valid) { diff --git a/compiler/luci/pass/src/QuantizeWeights.cpp b/compiler/luci/pass/src/QuantizeWeights.cpp index 29cdaff..59329c1 100644 --- a/compiler/luci/pass/src/QuantizeWeights.cpp +++ b/compiler/luci/pass/src/QuantizeWeights.cpp @@ -92,9 +92,8 @@ void asym_wquant_per_channel(CircleConst *node, std::vector &min, // TODO Reduce duplicate code with QuantizeDequantizeWeights void sym_wquant_per_channel(CircleConst *node, std::vector &min, std::vector &max, - std::vector &scaling_factor, std::vector &zp, - std::vector &nudged_min, std::vector &nudged_max, - int32_t &channel_dim_index) + std::vector &scaling_factor, std::vector &nudged_min, + std::vector &nudged_max, int32_t &channel_dim_index) { assert(node->dtype() == loco::DataType::FLOAT32); const int32_t kMaxScale = std::numeric_limits::max(); @@ -105,7 +104,7 @@ void sym_wquant_per_channel(CircleConst *node, std::vector &min, std::vec for (size_t i = 0; i < min.size(); ++i) { - compute_sym_scale_zp(min[i], max[i], scaling_factor[i], zp[i], nudged_min[i], nudged_max[i]); + compute_sym_scale(min[i], max[i], scaling_factor[i], nudged_min[i], nudged_max[i]); } auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { @@ -383,7 +382,7 @@ void QuantizeWeights::quantize_weights(luci::CircleConst *weights) } else { - sym_wquant_per_channel(weights, min, max, scaling_factor, zp, nudged_min, nudged_max, + sym_wquant_per_channel(weights, min, max, scaling_factor, nudged_min, nudged_max, channel_dim_index); } diff --git a/compiler/luci/pass/src/QuantizeWeightsOnly.cpp b/compiler/luci/pass/src/QuantizeWeightsOnly.cpp new file mode 100644 index 0000000..e69a7b6 --- /dev/null +++ b/compiler/luci/pass/src/QuantizeWeightsOnly.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QuantizeWeightsOnly.h" +#include "QuantizationUtils.h" + +#include +#include + +#include +#include +#include +#include + +using namespace luci; + +namespace +{ + +using IterFunc = std::function; + +void iterate_per_channel(CircleConst *node, int32_t &channel_dim_index, IterFunc func) +{ + loco::TensorShape dimension; + dimension.rank(4); + uint32_t indices[4] = { + 0, + }; + + if (!get_channel_dim_index(node, dimension, channel_dim_index)) + { + assert(false); + return; + } + + for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) + { + for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) + { + for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) + { + for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) + { + func(indices, dimension, channel_dim_index); + } + } + } + } +} + +// TODO Reduce duplicate code with QuantizeDequantizeWeights +template +void sym_wquant_per_channel(CircleConst *node, std::vector &min, std::vector &max, + std::vector &scaling_factor, std::vector &nudged_min, + std::vector &nudged_max, int32_t &channel_dim_index) +{ + assert(node->dtype() == loco::DataType::FLOAT32); + assert(out_type == loco::DataType::S8 || out_type == loco::DataType::S16); + const int32_t kMaxScale = (out_type == loco::DataType::S8) ? std::numeric_limits::max() + : std::numeric_limits::max(); + const int32_t kMinScale = -kMaxScale; + + uint32_t size = node->size(); + std::vector quantized_values(size); + + for (size_t i = 0; i < min.size(); ++i) + { + compute_sym_scale(min[i], max[i], scaling_factor[i], nudged_min[i], nudged_max[i], out_type); + } + + auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; + auto data = node->at(cal_offset(dimension, indices)); + data = data < nudged_min[channel_idx] ? nudged_min[channel_idx] : data; + data = data > nudged_max[channel_idx] ? nudged_max[channel_idx] : data; + quantized_values[cal_offset(dimension, indices)] = + static_cast(std::round(data * scaling_factor_inv)); + }; + + iterate_per_channel(node, channel_dim_index, quantize); + + node->dtype(out_type); // change the type of tensor + node->size(size); // resize tensor + for (uint32_t i = 0; i < size; ++i) + { + node->at(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + } +} + +void cal_minmax_per_channel(CircleConst *node, std::vector &min, std::vector &max, + int32_t &channel_dim_index) +{ + loco::TensorShape dimension; + dimension.rank(4); + + if (!get_channel_dim_index(node, dimension, channel_dim_index)) + { + throw std::runtime_error("Failed to find channel index in " + node->name()); + } + auto size = dimension.dim(channel_dim_index).value(); + + std::vector has_min_max_value(size, false); + min.resize(size); + max.resize(size); + + auto cal_minmax = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + auto data = node->at(cal_offset(dimension, indices)); + if (has_min_max_value[channel_idx]) + { + min[channel_idx] = data < min[channel_idx] ? data : min[channel_idx]; + max[channel_idx] = data > max[channel_idx] ? data : max[channel_idx]; + } + else + { + min[channel_idx] = data; + max[channel_idx] = data; + has_min_max_value[channel_idx] = true; + } + }; + + iterate_per_channel(node, channel_dim_index, cal_minmax); +} + +} // namespace + +namespace luci +{ + +void QuantizeWeightsOnly::quantize_weights(luci::CircleConst *weights) +{ + // Find min/max per channel-wise + if (granularity == QuantizationGranularity::ChannelWise) + { + auto quantparam = weights->quantparam(); + if (quantparam == nullptr) + { + // Find min/max on the fly + // NOTE This is for the case when QuantizeDequantizeWeights is skipped + // TODO Reduce duplicate codes + std::vector min; + std::vector max; + int32_t channel_dim_index = 0; + + cal_minmax_per_channel(weights, min, max, channel_dim_index); + + std::vector nudged_min(min.size()); + std::vector nudged_max(min.size()); + std::vector scaling_factor(min.size()); + std::vector zp(min.size()); + + if (output_type == loco::DataType::S8) + { + sym_wquant_per_channel(weights, min, max, scaling_factor, nudged_min, + nudged_max, channel_dim_index); + } + else if (output_type == loco::DataType::S16) + { + sym_wquant_per_channel(weights, min, max, scaling_factor, nudged_min, + nudged_max, channel_dim_index); + } + else + { + throw std::runtime_error("Weights-only quantization supports s8 and s16"); + } + + auto quantparam = std::make_unique(); + quantparam->scale = scaling_factor; + quantparam->zerop = zp; + quantparam->quantized_dimension = channel_dim_index; + weights->quantparam(std::move(quantparam)); + + return; + } + } + else + throw std::runtime_error("Weights-only quantization does not support layer-wise"); +} + +void QuantizeWeightsOnly::visit(luci::CircleConv2D *node) +{ + LOGGER(l); + INFO(l) << "QuantizeWeightsOnly visits node: " << node->name() << std::endl; + + auto weights = loco::must_cast(node->filter()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->filter(new_weights); + quantize_weights(new_weights); + } +} + +void QuantizeWeightsOnly::visit(luci::CircleDepthwiseConv2D *node) +{ + LOGGER(l); + INFO(l) << "QuantizeWeightsOnly visits node: " << node->name() << std::endl; + + auto weights = loco::must_cast(node->filter()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->filter(new_weights); + quantize_weights(new_weights); + } +} + +void QuantizeWeightsOnly::visit(luci::CircleNode *) {} + +} // namespace luci diff --git a/compiler/luci/pass/src/QuantizeWeightsOnly.h b/compiler/luci/pass/src/QuantizeWeightsOnly.h new file mode 100644 index 0000000..ff6ad32 --- /dev/null +++ b/compiler/luci/pass/src/QuantizeWeightsOnly.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_QUANTIZE_WEIGHTS_ONLY_H__ +#define __LUCI_QUANTIZE_WEIGHTS_ONLY_H__ + +#include +#include + +namespace luci +{ + +/** + * @brief QuantizeWeightsOnly quantizes tensors for weights + * @details Find min/max values on the fly and then quantize + */ +struct QuantizeWeightsOnly final : public luci::CircleNodeMutableVisitor +{ + QuantizeWeightsOnly(loco::DataType input, loco::DataType output, QuantizationGranularity gr) + : input_type(input), output_type(output), granularity(gr) + { + } + + loco::DataType input_type; + loco::DataType output_type; + QuantizationGranularity granularity; + +private: + void quantize_weights(luci::CircleConst *weights); + + void visit(luci::CircleConv2D *node); + void visit(luci::CircleDepthwiseConv2D *node); + void visit(luci::CircleNode *); +}; + +} // namespace luci + +#endif // __LUCI_QUANTIZE_WEIGHTS_ONLY_H__ diff --git a/compiler/luci/pass/src/QuantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeWeightsPass.cpp new file mode 100644 index 0000000..9ac203e --- /dev/null +++ b/compiler/luci/pass/src/QuantizeWeightsPass.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 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 "luci/Pass/QuantizeWeightsPass.h" +#include "QuantizeWeightsOnly.h" +#include "QuantizationUtils.h" + +#include + +namespace luci +{ + +bool QuantizeWeightsPass::run(loco::Graph *g) +{ + LOGGER(l); + INFO(l) << "QuantizeWeightsPass Start" << std::endl; + + if (_ctx->input_model_dtype != loco::DataType::FLOAT32) + throw std::runtime_error("Weights-only quantization supports float32 input only"); + + // Quantize weights + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast(node); + QuantizeWeightsOnly qw(_ctx->input_model_dtype, _ctx->output_model_dtype, _ctx->granularity); + circle_node->accept(&qw); + } + + INFO(l) << "QuantizeWeightsPass End" << std::endl; + return false; // one time run +} + +} // namespace luci diff --git a/compiler/luci/pass/src/QuantizeWeightsPass.test.cpp b/compiler/luci/pass/src/QuantizeWeightsPass.test.cpp new file mode 100644 index 0000000..058e029 --- /dev/null +++ b/compiler/luci/pass/src/QuantizeWeightsPass.test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/QuantizeWeightsPass.h" +#include + +#include + +namespace +{ +struct QuantizeWeightsPassTest : public ::testing::Test +{ + /** + * nconv graph + * + * [CircleInput] + * | + * | + * [CircleConv2D] + * | + * | + * [CircleOutput] + */ + void MakeGraph() + { + const int N = 1; + const int H = 4; + const int W = 4; + const int C = 3; // IC = OC + + // graph input and output + auto graph_input = _g.inputs()->create(); + auto graph_output = _g.outputs()->create(); + + // CircleInput + auto input = _g.nodes()->create(); + input->index(graph_input->index()); + input->shape({N, H, W, C}); + input->dtype(loco::DataType::FLOAT32); + input->name("input"); + + // CircleConv2D + auto conv = _g.nodes()->create(); + conv->input(input); + auto bias = _g.nodes()->create(); + bias->dtype(loco::DataType::FLOAT32); + bias->shape({C}); + bias->name("conv_bias"); + conv->bias(bias); + auto weight = _g.nodes()->create(); + weight->dtype(loco::DataType::FLOAT32); + weight->shape({C, H, W, C}); + weight->size(C * H * W * C); + conv->filter(weight); + conv->padding(luci::Padding::SAME); + conv->fusedActivationFunction(luci::FusedActFunc::NONE); + conv->dtype(loco::DataType::FLOAT32); + conv->name("nconv"); + + // CircleOutput + auto output = _g.nodes()->create(); + output->index(graph_output->index()); + output->from(conv); + output->shape({N, H, W, C}); + output->dtype(loco::DataType::FLOAT32); + output->name("output"); + } + virtual void SetUp() { MakeGraph(); } + loco::Graph _g; +}; + +} // namespace + +TEST_F(QuantizeWeightsPassTest, name) +{ + luci::QuantizeWeightsPass pass(loco::DataType::FLOAT32, loco::DataType::S8, + luci::QuantizationGranularity::ChannelWise); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(QuantizeWeightsPassTest, name_ctx) +{ + auto ctx = std::make_unique(); + { + ctx->input_model_dtype = loco::DataType::FLOAT32; + ctx->output_model_dtype = loco::DataType::S8; + ctx->granularity = luci::QuantizationGranularity::ChannelWise; + } + + luci::QuantizeWeightsPass pass(std::move(ctx)); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(QuantizeWeightsPassTest, run_input_U8_NEG) +{ + loco::Graph g; + luci::QuantizeWeightsPass pass(loco::DataType::U8, loco::DataType::S8, + luci::QuantizationGranularity::ChannelWise); + EXPECT_THROW(pass.run(&_g), std::runtime_error); +} + +TEST_F(QuantizeWeightsPassTest, run_output_f32_NEG) +{ + loco::Graph g; + luci::QuantizeWeightsPass pass(loco::DataType::FLOAT32, loco::DataType::FLOAT32, + luci::QuantizationGranularity::ChannelWise); + EXPECT_THROW(pass.run(&_g), std::runtime_error); +} diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp index c68e067..4f4edaf 100644 --- a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp +++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp @@ -101,7 +101,7 @@ luci::CircleQuantize *create_quantize_op(luci::CircleNode *node, loco::DataType else { assert(out_type == loco::DataType::S16); - compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + compute_sym_scale(min, max, scaling_factor, nudged_min, nudged_max); } auto quantparam = std::make_unique(); @@ -271,6 +271,7 @@ private: INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleFloor, x) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleFullyConnected, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleGather, params) + INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleGelu, features) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleInstanceNorm, input) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleLeakyRelu, features) INSERT_QUANTIZE_TO_UNARY_OP(luci::CircleLocalResponseNormalization, input) @@ -433,7 +434,7 @@ void QuantizeWithMinMaxPass::set_input_type(loco::Graph *g) const else { assert(user_given_dtype == loco::DataType::S16); - compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max); + compute_sym_scale(min, max, scaling_factor, nudged_min, nudged_max); } input->quantparam()->scale[0] = scaling_factor; input->quantparam()->zerop[0] = zp; @@ -479,15 +480,15 @@ void QuantizeWithMinMaxPass::set_output_type(loco::Graph *g) const if (user_given_dtype == loco::DataType::FLOAT32) { auto dequant_op = create_dequantize(from); - loco::replace(from).with(dequant_op); dequant_op->input(from); + output->from(dequant_op); } else { // Insert Quantize Op for non-float32 output_type auto quant_op = create_quantize_op(from, user_given_dtype); - loco::replace(from).with(quant_op); quant_op->input(from); + output->from(quant_op); // TODO Set a proper origin (Quantize should have its own Origin) luci::add_origin(quant_op, luci::get_origin(from)); @@ -629,6 +630,13 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g) for (auto node : loco::active_nodes(loco::output_nodes(g))) { auto circle_node = loco::must_cast(node); + + // At this point, all activations have to be quantized. + // Un-quantized nodes are not the quantization target (ex: int32 tensor), + // so we skip them + if (circle_node->quantparam() == nullptr) + continue; + QuantizeSpecialActivation qsa(_ctx->input_model_dtype, quantize_dtype(circle_node)); circle_node->accept(&qsa); } diff --git a/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp index 05ec317..ae02edb 100644 --- a/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp +++ b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp @@ -66,6 +66,8 @@ template luci::CircleConst *create_dummy_const(loco::Graph *g, luci::te // Fill with index node->at(i) = static_cast(i); break; + default: + break; } } } @@ -470,15 +472,15 @@ public: void init(void) override { TestIOGraph::init({32}, {32}); - _begin = g()->nodes()->create(); + _begin = g()->nodes()->template create(); { _begin->dtype(indexT); } - _size = g()->nodes()->create(); + _size = g()->nodes()->template create(); { _size->dtype(indexT); } - _slice = g()->nodes()->create(); + _slice = g()->nodes()->template create(); { _slice->input(input()); _slice->begin(_begin); @@ -595,6 +597,31 @@ private: luci::CircleConst *_strides = nullptr; }; +class SumTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({4, 3, 2}, {2}); + + _axis = create_const(g(), {2}, {1, 0}); + _sum = g()->nodes()->create(); + { + _sum->input(input()); + _sum->reduction_indices(_axis); + _sum->name("test"); + _sum->keep_dims(false); + } + output()->from(_sum); + + set_minmax_to_non_const(g(), -1, 1); + } + +private: + luci::CircleSum *_sum = nullptr; + luci::CircleConst *_axis = nullptr; +}; + class ReshapeTestGraph final : public SimpleTestGraph { public: @@ -669,11 +696,11 @@ public: TestIOGraph::init({32}, {1}); // output dtype is float by default, but ArgMax should have indexType (s32/s64) output()->dtype(indexT); - _dimension = g()->nodes()->create(); + _dimension = g()->nodes()->template create(); { _dimension->dtype(indexT); } - _argmax = g()->nodes()->create(); + _argmax = g()->nodes()->template create(); { _argmax->input(input()); _argmax->dimension(_dimension); @@ -978,7 +1005,7 @@ public: TestIOGraph::init({32}, {32}); output()->dtype(loco::DataType::BOOL); _y = create_dummy_const(g(), {32}); - _op = g()->nodes()->create(); + _op = g()->nodes()->template create(); { _op->x(input()); _op->y(_y); @@ -1011,7 +1038,7 @@ public: input()->dtype(loco::DataType::BOOL); output()->dtype(loco::DataType::BOOL); _y = create_dummy_const(g(), {32}); - _op = g()->nodes()->create(); + _op = g()->nodes()->template create(); { _op->x(input()); _op->y(_y); @@ -1315,7 +1342,7 @@ public: TypedTestGraph::init(T, {32}, {32}); _const = create_dummy_const(g(), {32}); - _mul = g()->nodes()->create(); + _mul = g()->nodes()->template create(); { _mul->x(input()); _mul->y(_const); @@ -1370,7 +1397,7 @@ public: TypedTestGraph::init(T, {32}, {32}); _const = create_dummy_const(g(), {32}); - _add = g()->nodes()->create(); + _add = g()->nodes()->template create(); { _add->x(input()); _add->y(_const); @@ -1786,6 +1813,34 @@ TEST(QuantizedModelVerifierTest, StridedSlice_wrong_granularity_NEG) SUCCEED(); } +TEST(QuantizedModelVerifierTest, Sum) +{ + TEST_WITH_GRAPH(SumTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SumTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SumTestGraph, Type::S16, Granularity::ChannelWise); + + TEST_WITH_LAYER_INFO(SumTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_LAYER_INFO(SumTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_LAYER_INFO(SumTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Sum_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SumTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SumTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SumTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Sum_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SumTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SumTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SumTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + TEST(QuantizedModelVerifierTest, ArgMax) { TEST_WITH_GRAPH(ArgMaxTestGraph, Type::U8, Granularity::LayerWise); diff --git a/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp index 93024f3..194893f 100644 --- a/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp +++ b/compiler/luci/pass/src/ReplaceNonConstFCWithBatchMatMulPass.test.cpp @@ -16,6 +16,8 @@ #include "luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h" +#include "helpers/CreateCircleConst.h" + #include #include @@ -26,52 +28,6 @@ namespace using namespace luci::test; -// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp -template -luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, - const std::vector &shape, - const std::vector &values) -{ - auto node = g->nodes()->create(); - node->dtype(dtype); - node->rank(shape.size()); - - uint32_t size = 1; - for (uint32_t i = 0; i < shape.size(); ++i) - { - node->dim(i) = shape.at(i); - size *= shape.at(i); - } - node->shape_status(luci::ShapeStatus::VALID); - -#define INIT_VALUES(DT) \ - { \ - node->size
(size); \ - for (uint32_t i = 0; i < values.size(); ++i) \ - node->at
(i) = values[i]; \ - } - - switch (dtype) - { - case loco::DataType::U8: - INIT_VALUES(loco::DataType::U8); - break; - case loco::DataType::S16: - INIT_VALUES(loco::DataType::S16); - break; - case loco::DataType::S32: - INIT_VALUES(loco::DataType::S32); - break; - case loco::DataType::FLOAT32: - INIT_VALUES(loco::DataType::FLOAT32) - break; - default: - INTERNAL_EXN("create_const_node called with unsupported type"); - break; - } - return node; -} - /** * Simple graph for test * @@ -104,7 +60,7 @@ public: _tr_y = g->nodes()->create(); _tr_y->a(_y); std::vector tr_val = {1, 0}; - _tr_y->perm(create_const_node(g, loco::DataType::S32, {2}, tr_val)); + _tr_y->perm(luci::create_const_node(g, loco::DataType::S32, {2}, tr_val)); _fc = g->nodes()->create(); _fc->input(_x); @@ -114,7 +70,7 @@ public: _fc->shape(r_shape); auto l = _fc->dim(_fc->rank() - 1).value(); std::vector bias_val(l, bv); - _fc->bias(create_const_node(g, loco::DataType::FLOAT32, {l}, bias_val)); + _fc->bias(luci::create_const_node(g, loco::DataType::FLOAT32, {l}, bias_val)); _fc->name("fc"); } diff --git a/compiler/luci/pass/src/ReplaceSubWithAddPass.cpp b/compiler/luci/pass/src/ReplaceSubWithAddPass.cpp index 6bd83f5..f9102d8 100644 --- a/compiler/luci/pass/src/ReplaceSubWithAddPass.cpp +++ b/compiler/luci/pass/src/ReplaceSubWithAddPass.cpp @@ -17,6 +17,7 @@ #include "luci/Pass/ReplaceSubWithAddPass.h" #include +#include #include namespace @@ -47,6 +48,7 @@ bool replace_sub_with_const_rhs(luci::CircleSub *sub) add->y(neg_const_rhs); add->name(sub->name()); add->fusedActivationFunction(sub->fusedActivationFunction()); + luci::add_origin(add, luci::get_origin(sub)); loco::replace(sub).with(add); return true; } diff --git a/compiler/luci/pass/src/RequantizePass.cpp b/compiler/luci/pass/src/RequantizePass.cpp index a565362..77c5532 100644 --- a/compiler/luci/pass/src/RequantizePass.cpp +++ b/compiler/luci/pass/src/RequantizePass.cpp @@ -32,37 +32,9 @@ namespace luci namespace { -// Check if the node is the bias of Conv2D, DepthwiseConv2D, or FullyConnected layer -bool is_bias(CircleConst *node) -{ - if (node == nullptr) - return false; - - auto succs = loco::succs(node); - if (succs.size() != 1) // assume bias is used by only one node - return false; - - for (auto out : succs) - { - auto conv = dynamic_cast(out); - if (conv != nullptr && conv->bias() == node) - return true; - - auto dw_conv = dynamic_cast(out); - if (dw_conv != nullptr && dw_conv->bias() == node) - return true; - - auto fc = dynamic_cast(out); - if (fc != nullptr && fc->bias() == node) - return true; - - auto tconv = dynamic_cast(out); - if (tconv != nullptr && tconv->bias() == node) - return true; - } - return false; -} - +// Requantize Non-const node from int8 to uint8 +// Original values: -128 ~ 127 +// After requantization: 0 ~ 255 void requant_nonconst_int8_to_uint8(CircleNode *circle_node) { assert(circle_node->dtype() == loco::DataType::S8); @@ -107,99 +79,48 @@ void requant_const_int8_to_uint8(CircleConst *node) } } +#define RETURN_UNLESS(cond) \ + if (not(cond)) \ + return; + /** - * @brief RequantizeNonConst requantizes tensors for activations + * @brief Requantize int8 quantized tensors to uint8 tensors */ -struct RequantizeNonConst final : public luci::CircleNodeMutableVisitor +struct RequantizeS8ToU8 final : public luci::CircleNodeMutableVisitor { - RequantizeNonConst(loco::DataType input, loco::DataType output) - : _input_type(input), _output_type(output) - { - } - - loco::DataType _input_type; - loco::DataType _output_type; - - // Requantize input tensors of each node - bool visit(luci::CircleNode *node) + // Requantize non-const tensors + void visit(luci::CircleNode *node) { LOGGER(l); - INFO(l) << "RequantizeNonConst visit node: " << node->name() << std::endl; - auto arity = node->arity(); - for (uint32_t i = 0; i < arity; i++) - { - auto input_node = node->arg(i); - auto circle_node = loco::must_cast(input_node); + INFO(l) << "RequantizeS8ToU8 visit non-const node: " << node->name() << std::endl; - // Check if this was quantized (only quantized tensors are requantized) - if (circle_node->quantparam() == nullptr) - continue; + // Ignore non-quantized tensors + RETURN_UNLESS(node->quantparam() != nullptr); - // Check if this is already requantized - if (circle_node->dtype() == _output_type) - continue; + // Check dtype is int8 + RETURN_UNLESS(node->dtype() == loco::DataType::S8); - // Check if this is not const (only non-const is requantized in this function) - auto circle_const = dynamic_cast(circle_node); - if (circle_const != nullptr) - continue; - - if (_input_type == loco::DataType::S8 && _output_type == loco::DataType::U8) - requant_nonconst_int8_to_uint8(circle_node); - } - return false; - } -}; - -/** - * @brief RequantizeConst requantizes tensors for weights - */ -struct RequantizeConst final : public luci::CircleNodeMutableVisitor -{ - RequantizeConst(loco::DataType input, loco::DataType output) - : _input_type(input), _output_type(output) - { + requant_nonconst_int8_to_uint8(node); } - loco::DataType _input_type; - loco::DataType _output_type; - - // Requantize input tensors of each node - bool visit(luci::CircleNode *node) + // Requantize const tensors + void visit(luci::CircleConst *node) { LOGGER(l); - INFO(l) << "RequantizeConst visit node: " << node->name() << std::endl; - auto arity = node->arity(); - for (uint32_t i = 0; i < arity; i++) - { - auto input_node = node->arg(i); - auto circle_node = loco::must_cast(input_node); + INFO(l) << "RequantizeS8ToU8 visit const node: " << node->name() << std::endl; - // Check if this was quantized (only quantized tensors are requantized) - if (circle_node->quantparam() == nullptr) - continue; + // Ignore non-quantized tensors + RETURN_UNLESS(node->quantparam() != nullptr); - // Check if this is already requantized - if (circle_node->dtype() == _output_type) - continue; + // Check dtype is int8 + RETURN_UNLESS(node->dtype() == loco::DataType::S8); - // Check if this is const (only const is requantized in this function) - auto circle_const = dynamic_cast(circle_node); - if (circle_const == nullptr) - continue; - - // Check if this is not bias - // bias is not requantized when int8 -> uint8 - if (is_bias(circle_const)) - continue; - - if (_input_type == loco::DataType::S8 && _output_type == loco::DataType::U8) - requant_const_int8_to_uint8(circle_const); - } - return false; + requant_const_int8_to_uint8(node); } }; +#undef RETURN_UNLESS + } // namespace bool RequantizePass::run(loco::Graph *g) @@ -207,20 +128,21 @@ bool RequantizePass::run(loco::Graph *g) LOGGER(l); INFO(l) << "RequantizePass Start" << std::endl; - // Requantize non-const (activations) - for (auto node : loco::active_nodes(loco::output_nodes(g))) + // Input: int8 model + // Output: uint8 model + if (_input_dtype == loco::DataType::S8 and _output_dtype == loco::DataType::U8) { - RequantizeNonConst rqnc(_input_dtype, _output_dtype); - auto circle_node = loco::must_cast(node); - circle_node->accept(&rqnc); + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + RequantizeS8ToU8 rq; + auto circle_node = loco::must_cast(node); + circle_node->accept(&rq); + } } - - // Requantize const (including weights, constants) - for (auto node : loco::active_nodes(loco::output_nodes(g))) + else { - RequantizeConst rqc(_input_dtype, _output_dtype); - auto circle_node = loco::must_cast(node); - circle_node->accept(&rqc); + // Ignore other cases + return false; } // Update output dtype @@ -228,7 +150,8 @@ bool RequantizePass::run(loco::Graph *g) for (auto node : loco::output_nodes(g)) { auto circle_node = loco::must_cast(node); - if (static_cast(circle_node->from())->dtype() == _output_dtype) + auto from_node = loco::must_cast(circle_node->from()); + if (from_node->dtype() == _output_dtype) { circle_node->dtype(_output_dtype); auto graph_output = graph_outputs->at(circle_node->index()); diff --git a/compiler/luci/pass/src/RequantizePass.test.cpp b/compiler/luci/pass/src/RequantizePass.test.cpp index d26743c..a9293ce 100644 --- a/compiler/luci/pass/src/RequantizePass.test.cpp +++ b/compiler/luci/pass/src/RequantizePass.test.cpp @@ -16,11 +16,167 @@ #include "luci/Pass/RequantizePass.h" +#include "helpers/CreateCircleConst.h" + +#include +#include +#include + +#include + #include +using namespace luci; +using namespace luci::test; + +namespace +{ + +/** + * Simple graph for test + * + * BEFORE + * + * [IFM (S8)] [W (S8)] [B (S32)] + * | | | + * +-------+--------+ + * | + * V + * [FC] + * | + * V + * [OFM(S8)] + * + * AFTER + * + * [IFM (U8)] [W (U8)] [B (S32)] + * | | | + * +-------+--------+ + * | + * V + * [FC] + * | + * V + * [OFM(U8)] + */ +struct S8FCGraphlet +{ +public: + S8FCGraphlet() = default; + virtual ~S8FCGraphlet() = default; + + void init(loco::Graph *g, const ShapeU32 out_shape, const ShapeU32 w_shape, + const ShapeU32 bias_shape) + { + _fc = g->nodes()->create(); + _fc->input(_x); + _x->dtype(loco::DataType::S8); + { + auto quantparam = std::make_unique(); + quantparam->scale.push_back(1.0); + quantparam->zerop.push_back(0); + quantparam->quantized_dimension = 0; + _x->quantparam(std::move(quantparam)); + } + + _weights = create_const_node(g, loco::DataType::S8, w_shape, 1.0); + { + auto w_qparam = std::make_unique(); + std::vector w_scale(_weights->dim(0).value(), 1.0); + std::vector w_zp(_weights->dim(0).value(), 0); + w_qparam->scale = w_scale; + w_qparam->zerop = w_zp; + w_qparam->quantized_dimension = 0; + _weights->quantparam(std::move(w_qparam)); + } + _fc->weights(_weights); + + _bias = create_const_node(g, loco::DataType::S32, bias_shape, 1.0); + { + auto b_qparam = std::make_unique(); + const auto bias_size = _bias->size(); + std::vector b_scale(bias_size, 1.0); + std::vector b_zp(bias_size, 0); + b_qparam->scale = b_scale; + b_qparam->zerop = b_zp; + b_qparam->quantized_dimension = 0; + _bias->quantparam(std::move(b_qparam)); + } + + _fc->fusedActivationFunction(luci::FusedActFunc::NONE); + _fc->dtype(loco::DataType::S8); + _fc->shape(out_shape); + _fc->bias(_bias); + _fc->name("fc"); + { + auto quantparam = std::make_unique(); + quantparam->scale.push_back(1.0); + quantparam->zerop.push_back(0); + quantparam->quantized_dimension = 0; + _fc->quantparam(std::move(quantparam)); + } + } + +public: + CircleFullyConnected *_fc = nullptr; + CircleInput *_x = nullptr; + CircleConst *_weights = nullptr; + CircleConst *_bias = nullptr; +}; + +struct S8FCGraph final : public TestIGraphlet, public TestOGraphlet, public S8FCGraphlet +{ + void init(const ShapeU32 in_shape, const ShapeU32 w_shape, const ShapeU32 out_shape, + const ShapeU32 bias_shape) + { + TestIGraphlet::init(g(), in_shape); + TestOGraphlet::init(g(), out_shape); + _x = input(); + S8FCGraphlet::init(g(), out_shape, w_shape, bias_shape); + output()->from(_fc); + } +}; + +class RequantizeS8ToU8FCTest : public ::testing::Test +{ +public: + S8FCGraph g; +}; + +} // namespace + TEST(RequantizePassTest, name) { luci::RequantizePass pass(loco::DataType::FLOAT32, loco::DataType::U8); auto const name = pass.name(); ASSERT_NE(nullptr, name); } + +TEST_F(RequantizeS8ToU8FCTest, FC) +{ + g.init({1, 18, 80} /* ifm shape */, {256, 80} /* weights shape*/, {18, 256} /* ofm shape */, + {1, 256} /* bias shape*/); + + luci::RequantizePass rq(loco::DataType::S8, loco::DataType::U8); + rq.run(g.g()); + + EXPECT_EQ(loco::DataType::U8, g._x->dtype()); + EXPECT_EQ(loco::DataType::U8, g._fc->dtype()); + EXPECT_EQ(loco::DataType::U8, g._weights->dtype()); + EXPECT_EQ(loco::DataType::S32, g._bias->dtype()); +} + +TEST_F(RequantizeS8ToU8FCTest, FC_wrong_dtype_NEG) +{ + g.init({1, 18, 80} /* ifm shape */, {256, 80} /* weights shape*/, {18, 256} /* ofm shape */, + {1, 256} /* bias shape*/); + + // Wrong dtype + luci::RequantizePass rq(loco::DataType::U8, loco::DataType::S8); + rq.run(g.g()); + + EXPECT_EQ(loco::DataType::S8, g._x->dtype()); + EXPECT_EQ(loco::DataType::S8, g._fc->dtype()); + EXPECT_EQ(loco::DataType::S8, g._weights->dtype()); + EXPECT_EQ(loco::DataType::S32, g._bias->dtype()); +} diff --git a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp index f618827..add55f6 100644 --- a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp +++ b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp @@ -16,6 +16,8 @@ #include "luci/Pass/ResolveCustomOpMatMulPass.h" +#include "helpers/CreateCircleConst.h" + #include #include @@ -29,51 +31,6 @@ namespace { -template -luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, - const std::vector &shape, - const std::vector &values) -{ - auto node = g->nodes()->create(); - node->dtype(dtype); - node->rank(shape.size()); - - uint32_t size = 1; - for (uint32_t i = 0; i < shape.size(); ++i) - { - node->dim(i) = shape.at(i); - size *= shape.at(i); - } - node->shape_status(luci::ShapeStatus::VALID); - -#define INIT_VALUES(DT) \ - { \ - node->size
(size); \ - for (uint32_t i = 0; i < values.size(); ++i) \ - node->at
(i) = values[i]; \ - } - - switch (dtype) - { - case loco::DataType::U8: - INIT_VALUES(loco::DataType::U8); - break; - case loco::DataType::S16: - INIT_VALUES(loco::DataType::S16); - break; - case loco::DataType::S32: - INIT_VALUES(loco::DataType::S32); - break; - case loco::DataType::FLOAT32: - INIT_VALUES(loco::DataType::FLOAT32) - break; - default: - INTERNAL_EXN("create_const_node called with unsupported type"); - break; - } - return node; -} - bool resolve_matmul(luci::CircleCustom *cop) { #define CHECK_OR_FALSE(condition) \ @@ -121,11 +78,12 @@ bool resolve_matmul(luci::CircleCustom *cop) if (transpose_a) { // Create a permutation constant node - std::vector perm; - for (uint32_t i = 0; i < circle_lhs->rank(); ++i) + std::vector perm; + const auto lhs_rank = static_cast(circle_lhs->rank()); + for (int32_t i = 0; i < lhs_rank; ++i) perm.push_back(i); std::swap(perm[circle_lhs->rank() - 1], perm[circle_lhs->rank() - 2]); - auto perm_node = create_const_node(graph, S32, {circle_lhs->rank()}, perm); + auto perm_node = luci::create_const_node(graph, S32, {circle_lhs->rank()}, perm); perm_node->name(name + "/lhs/Transpose/perm"); // Now make a transpose node auto transpose_node = graph->nodes()->create(); @@ -141,8 +99,8 @@ bool resolve_matmul(luci::CircleCustom *cop) // in row-major order, thus we need to convert between them. if (!transpose_b) { - const std::vector perm{1, 0}; - auto perm_node = create_const_node(graph, S32, {2}, perm); + const std::vector perm{1, 0}; + auto perm_node = luci::create_const_node(graph, S32, {2}, perm); perm_node->name(name + "/rhs/Transpose/perm"); auto transpose_node = graph->nodes()->create(); transpose_node->a(rhs); diff --git a/compiler/luci/pass/src/SubstituteSplitVToSplitPass.test.cpp b/compiler/luci/pass/src/SubstituteSplitVToSplitPass.test.cpp index 6e30103..43f9cc1 100644 --- a/compiler/luci/pass/src/SubstituteSplitVToSplitPass.test.cpp +++ b/compiler/luci/pass/src/SubstituteSplitVToSplitPass.test.cpp @@ -16,6 +16,8 @@ #include "luci/Pass/SubstituteSplitVToSplitPass.h" +#include "helpers/CreateCircleConst.h" + #include #include @@ -30,51 +32,6 @@ const int C = 32; const int H = 8; const int W = 8; -// Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp -template -luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, - const std::vector &shape, - const std::vector &values) -{ - auto node = g->nodes()->create(); - node->dtype(dtype); - node->rank(shape.size()); - - uint32_t size = 1; - for (uint32_t i = 0; i < shape.size(); ++i) - { - node->dim(i) = shape.at(i); - size *= shape.at(i); - } - node->shape_status(luci::ShapeStatus::VALID); - -#define INIT_VALUES(DT) \ - { \ - node->size
(size); \ - for (uint32_t i = 0; i < values.size(); ++i) \ - node->at
(i) = values[i]; \ - } - - switch (dtype) - { - case loco::DataType::U8: - INIT_VALUES(loco::DataType::U8); - break; - case loco::DataType::S16: - INIT_VALUES(loco::DataType::S16); - break; - case loco::DataType::S32: - INIT_VALUES(loco::DataType::S32); - break; - case loco::DataType::FLOAT32: - INIT_VALUES(loco::DataType::FLOAT32) - break; - default: - INTERNAL_EXN("create_const_node called with unsupported type"); - break; - } - return node; -} /** * graph having SplitV operator * @@ -95,10 +52,10 @@ public: void init(loco::Graph *g) { const std::vector splits{16, 16}; - auto size_splits = create_const_node(g, loco::DataType::S32, {2}, splits); + auto size_splits = luci::create_const_node(g, loco::DataType::S32, {2}, splits); const std::vector dim{3}; - auto split_dim = create_const_node(g, loco::DataType::S32, {1}, dim); + auto split_dim = luci::create_const_node(g, loco::DataType::S32, {1}, dim); _sv = g->nodes()->create(); _sv->size_splits(size_splits); diff --git a/compiler/luci/pass/src/VerifyQuantizedBiasScale.cpp b/compiler/luci/pass/src/VerifyQuantizedBiasScale.cpp index e65d576..d40c19b 100644 --- a/compiler/luci/pass/src/VerifyQuantizedBiasScale.cpp +++ b/compiler/luci/pass/src/VerifyQuantizedBiasScale.cpp @@ -31,7 +31,7 @@ namespace bool same(float a, float b) { constexpr float epsilon = 1e-10; - return abs(a - b) < epsilon; + return std::abs(a - b) < epsilon; } // Check bias scale = input scale * weight scale diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h b/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h index 6bf7ff6..cc618bf 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h +++ b/compiler/luci/pass/src/VerifyQuantizedNodeGranularity.h @@ -298,6 +298,13 @@ private: return true; } + bool visit(const luci::CircleSum *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + bool visit(const luci::CircleArgMax *node) { // node's output is index, thus not quantized @@ -333,6 +340,13 @@ private: return true; } + bool visit(const luci::CircleGelu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->features())); + return true; + } + bool visit(const luci::CircleGreater *node) { RETURN_FALSE_UNLESS(is_lwq(node->x())); diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp b/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp index 3ce3255..4bad952 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp +++ b/compiler/luci/pass/src/VerifyQuantizedNodeType.cpp @@ -181,6 +181,12 @@ bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleFullyCon } template +bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleGelu *node) +{ + return group_has_type(node, Qtype); +} + +template bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleGreater *node) { RETURN_FALSE_UNLESS(has_type(node, loco::DataType::BOOL)) @@ -454,6 +460,15 @@ bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleStridedS } template +bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleSum *node) +{ + RETURN_FALSE_UNLESS(has_type(node, Qtype)) + RETURN_FALSE_UNLESS(has_type(node->input(), Qtype)) + RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), loco::DataType::S32)) + return true; +} + +template bool VerifyQuantizedNodeTypeBase::visit(const luci::CircleTranspose *node) { RETURN_FALSE_UNLESS(has_type(node, Qtype)) diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeType.h b/compiler/luci/pass/src/VerifyQuantizedNodeType.h index 789d3c7..03f1e1d 100644 --- a/compiler/luci/pass/src/VerifyQuantizedNodeType.h +++ b/compiler/luci/pass/src/VerifyQuantizedNodeType.h @@ -88,6 +88,7 @@ private: bool visit(const luci::CircleFloor *node); bool visit(const luci::CircleFloorDiv *node); bool visit(const luci::CircleFullyConnected *node); + bool visit(const luci::CircleGelu *node); bool visit(const luci::CircleGreater *node); bool visit(const luci::CircleGreaterEqual *node); bool visit(const luci::CircleInstanceNorm *node); @@ -119,6 +120,7 @@ private: bool visit(const luci::CircleSplitVOut *node); bool visit(const luci::CircleSqrt *node); bool visit(const luci::CircleStridedSlice *node); + bool visit(const luci::CircleSum *node); bool visit(const luci::CircleTranspose *node); bool visit(const luci::CircleTransposeConv *node); bool visit(const luci::CircleUnpack *node); diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALFill.h b/compiler/luci/pass/src/helpers/CreateCircleConst.cpp similarity index 68% rename from onert-micro/luci-interpreter/pal/cmsisnn/PALFill.h rename to compiler/luci/pass/src/helpers/CreateCircleConst.cpp index c9eb33b..bf1b0ba 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALFill.h +++ b/compiler/luci/pass/src/helpers/CreateCircleConst.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +14,7 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_FILL_H -#define LUCI_INTERPRETER_PAL_FILL_H +#include "CreateCircleConst.h" -#include "PALreference_ops.h" - -#endif // LUCI_INTERPRETER_PAL_FILL_H +// NOTE Do NOT delete this file; this file enforces compiler to check whether 'CreateCircleConst.h' +// is complete. diff --git a/compiler/luci/pass/src/helpers/CreateCircleConst.h b/compiler/luci/pass/src/helpers/CreateCircleConst.h new file mode 100644 index 0000000..89c1a47 --- /dev/null +++ b/compiler/luci/pass/src/helpers/CreateCircleConst.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PASS_HELPERS_CREATE_CIRCLE_CONST_H__ +#define __LUCI_PASS_HELPERS_CREATE_CIRCLE_CONST_H__ + +#include + +#include "TypeMapper.h" + +#include + +namespace luci +{ + +// Create CircleConst filled with a single value +// Never return nullptr +// TODO Remove dtype from the argument +template +CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, + const std::vector &shape, const T value) +{ + auto node = g->nodes()->create(); + node->dtype(dtype); + node->rank(shape.size()); + + uint32_t size = 1; + for (uint32_t i = 0; i < shape.size(); ++i) + { + node->dim(i) = shape.at(i); + size *= shape.at(i); + } + node->shape_status(ShapeStatus::VALID); + + node->size::get()>(size); + for (uint32_t i = 0; i < size; i++) + { + node->at::get()>(i) = value; + } + + return node; +} + +// Create CircleConst filled with values +// Never return nullptr +// TODO Remove dtype from the argument +template +luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, + const std::vector &shape, + const std::vector &values) +{ + auto node = g->nodes()->create(); + node->dtype(dtype); + node->rank(shape.size()); + + uint32_t size = 1; + for (uint32_t i = 0; i < shape.size(); ++i) + { + node->dim(i) = shape.at(i); + size *= shape.at(i); + } + node->shape_status(luci::ShapeStatus::VALID); + + node->size::get()>(size); + for (uint32_t i = 0; i < size; i++) + { + node->at::get()>(i) = values[i]; + } + + return node; +} + +} // namespace luci + +#endif // __LUCI_PASS_HELPERS_CREATE_CIRCLE_CONST_H__ diff --git a/compiler/luci/pass/src/helpers/TypeMapper.h b/compiler/luci/pass/src/helpers/TypeMapper.h index 90760e9..a3e27d2 100644 --- a/compiler/luci/pass/src/helpers/TypeMapper.h +++ b/compiler/luci/pass/src/helpers/TypeMapper.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef __LUCI_PASS_HELPERS_TYPE_MAPPER_H__ +#define __LUCI_PASS_HELPERS_TYPE_MAPPER_H__ + #include #include @@ -75,3 +78,5 @@ template <> struct TypeMapper }; } // namespace luci + +#endif // __LUCI_PASS_HELPERS_TYPE_MAPPER_H__ diff --git a/compiler/luci/requires.cmake b/compiler/luci/requires.cmake index f3546b0..a71d448 100644 --- a/compiler/luci/requires.cmake +++ b/compiler/luci/requires.cmake @@ -4,7 +4,7 @@ require("loco") require("locop") require("logo") require("logo-core") -require("mio-circle04") +require("mio-circle06") require("luci-compute") require("oops") require("hermes") diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h index 2c11209..92c5fb0 100644 --- a/compiler/luci/service/include/luci/Service/CircleShapeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h @@ -79,6 +79,7 @@ public: // loco::TensorShape visit(const luci::CircleGatherNd *node) final; // loco::TensorShape visit(const luci::CircleGreater *node) final; // loco::TensorShape visit(const luci::CircleGreaterEqual *node) final; + // loco::TensorShape visit(const luci::CircleHardSwish *node) final; // loco::TensorShape visit(const luci::CircleIf *node) final; // loco::TensorShape visit(const luci::CircleL2Normalize *node) final; // loco::TensorShape visit(const luci::CircleL2Pool2D *node) final; diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h index e0ceabe..4f4ab0f 100644 --- a/compiler/luci/service/include/luci/Service/CircleTypeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h @@ -78,6 +78,7 @@ public: // loco::DataType visit(const luci::CircleGatherNd *node) final; // loco::DataType visit(const luci::CircleGreater *node) final; // loco::DataType visit(const luci::CircleGreaterEqual *node) final; + // loco::DataType visit(const luci::CircleHardSwish *node) final; // loco::DataType visit(const luci::CircleIf *node) final; // loco::DataType visit(const luci::CircleL2Normalize *node) final; // loco::DataType visit(const luci::CircleL2Pool2D *node) final; diff --git a/compiler/luci/service/include/luci/Service/Validate.h b/compiler/luci/service/include/luci/Service/Validate.h index c6a2b0e..815e5e3 100644 --- a/compiler/luci/service/include/luci/Service/Validate.h +++ b/compiler/luci/service/include/luci/Service/Validate.h @@ -39,6 +39,10 @@ bool validate_unique_name(luci::Module *); bool validate(luci::Module *); +bool validate_shape(loco::Graph *); + +bool validate_shape(luci::Module *); + } // namespace luci #endif // __LUCI_SERVICE_VALIDATE_H__ diff --git a/compiler/luci/service/src/CircleCloneNode.h b/compiler/luci/service/src/CircleCloneNode.h index 95f06db..e0b4dbc 100644 --- a/compiler/luci/service/src/CircleCloneNode.h +++ b/compiler/luci/service/src/CircleCloneNode.h @@ -102,8 +102,10 @@ public: public: luci::CircleNode *visit(const luci::CircleGather *) final; luci::CircleNode *visit(const luci::CircleGatherNd *) final; + luci::CircleNode *visit(const luci::CircleGelu *) final; luci::CircleNode *visit(const luci::CircleGreater *) final; luci::CircleNode *visit(const luci::CircleGreaterEqual *) final; + luci::CircleNode *visit(const luci::CircleHardSwish *) final; luci::CircleNode *visit(const luci::CircleIf *) final; luci::CircleNode *visit(const luci::CircleNode *) final { return nullptr; } diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp index 9d4dbab..d56886c 100644 --- a/compiler/luci/service/src/CircleShapeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp @@ -284,7 +284,7 @@ template loco::NodeShape infer_arg_maxmin(const CIRCLENODE *n assert(select_axis < input_shape.rank()); if (select_axis < 0) - select_axis += input_shape.rank(); + select_axis += static_cast(input_shape.rank()); // NOTE select_axis is removed loco::TensorShape shape_output; @@ -1287,7 +1287,7 @@ loco::NodeShape infer_slice(const luci::CircleSlice *node) auto size = vect_size.at(idx); if (size == -1) { - size = input_shape.dim(idx).value() - vect_begin.at(idx); + size = static_cast(input_shape.dim(idx).value()) - vect_begin.at(idx); } output_shape.dim(idx) = size; } @@ -1874,7 +1874,7 @@ loco::NodeShape infer_split_v_out(const luci::CircleSplitVOut *node) assert(0 <= index_this && index_this < split->num_split()); auto split_depth = size_splits->at(index_this); if (split_depth == -1) - split_depth = input_size - size_splits_sum; + split_depth = static_cast(input_size) - static_cast(size_splits_sum); loco::TensorShape output_shape = split_shape; @@ -2087,10 +2087,24 @@ public: loco::NodeShape visit(const luci::CircleGatherNd *node) final { return infer_gather_nd(node); } + loco::NodeShape visit(const luci::CircleGelu *node) final + { + auto input_shape = luci::shape_get(node->features()).as(); + + return loco::NodeShape{input_shape}; + } + loco::NodeShape visit(const luci::CircleGreater *node) final { return broadcast_xy(node); } loco::NodeShape visit(const luci::CircleGreaterEqual *node) final { return broadcast_xy(node); } + loco::NodeShape visit(const luci::CircleHardSwish *node) final + { + auto input_shape = luci::shape_get(node->features()).as(); + + return loco::NodeShape{input_shape}; + } + loco::NodeShape visit(const luci::CircleIf *node) final { // Shape of CircleIf is not used. Just use input 0 diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp index 44c9330..bd3feb9 100644 --- a/compiler/luci/service/src/CircleTypeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp @@ -172,10 +172,20 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitorparams()); } + loco::DataType visit(const luci::CircleGelu *node) final + { + return luci::dtype_get(node->features()); + } + loco::DataType visit(const luci::CircleGreater *) final { return loco::DataType::BOOL; } loco::DataType visit(const luci::CircleGreaterEqual *) final { return loco::DataType::BOOL; } + loco::DataType visit(const luci::CircleHardSwish *node) final + { + return luci::dtype_get(node->features()); + } + loco::DataType visit(const luci::CircleIf *node) final { // Type of If is not used. Just use input 0 diff --git a/compiler/luci/service/src/Nodes/CircleGelu.cpp b/compiler/luci/service/src/Nodes/CircleGelu.cpp new file mode 100644 index 0000000..62a0d40 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGelu.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNodeLet::visit(const luci::CircleGelu *node) +{ + auto *cloned = _graph->nodes()->create(); + if (cloned != nullptr) + cloned->approximate(node->approximate()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleGelu.test.cpp b/compiler/luci/service/src/Nodes/CircleGelu.test.cpp new file mode 100644 index 0000000..a043b2a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGelu.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/Service/CircleNodeClone.h" + +#include + +TEST(CloneNodeTest, clone_Gelu) +{ + auto g = loco::make_graph(); + auto node_gelu = g->nodes()->create(); + node_gelu->approximate(false); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_gelu, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_gelu = dynamic_cast(cloned); + ASSERT_NE(nullptr, cloned_gelu); + ASSERT_EQ(node_gelu->approximate(), cloned_gelu->approximate()); +} diff --git a/compiler/luci/service/src/Nodes/CircleHardSwish.cpp b/compiler/luci/service/src/Nodes/CircleHardSwish.cpp new file mode 100644 index 0000000..bbc466e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleHardSwish.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNodeLet::visit(const luci::CircleHardSwish *) +{ + return _graph->nodes()->create(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleHardSwish.test.cpp b/compiler/luci/service/src/Nodes/CircleHardSwish.test.cpp new file mode 100644 index 0000000..b79386b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleHardSwish.test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/Service/CircleNodeClone.h" + +#include +#include +#include + +#include + +#include + +TEST(ShapeRuleTest, simple_hardswish) +{ + luci::CircleInput input; + luci::CircleHardSwish hard_swish; + + input.shape({3, 4}); + input.shape_status(luci::ShapeStatus::VALID); + + hard_swish.features(&input); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&hard_swish, shape)); + ASSERT_EQ(2, shape.rank()); + ASSERT_EQ(3, shape.dim(0).value()); + ASSERT_EQ(4, shape.dim(1).value()); +} + +TEST(DataTypeRuleTest, simple_hardswish) +{ + luci::CircleInput input; + luci::CircleHardSwish hard_swish; + + input.dtype(loco::DataType::S32); + + hard_swish.features(&input); + + loco::DataType dtype; + luci::tinf::Rule type_inf_rule; + + ASSERT_TRUE(type_inf_rule.infer(&hard_swish, dtype)); + ASSERT_EQ(loco::DataType::S32, dtype); +} + +TEST(CloneNodeTest, clone_HardSwish) +{ + auto g = loco::make_graph(); + auto node_hardswish = g->nodes()->create(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_hardswish, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_hardswish = dynamic_cast(cloned); + ASSERT_NE(nullptr, cloned_hardswish); +} diff --git a/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp index 5d2fa48..73aad2e 100644 --- a/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp +++ b/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp @@ -30,6 +30,7 @@ luci::CircleNode *CloneNodeLet::visit(const luci::CircleTransposeConv cloned->padding(node->padding()); cloned->stride()->h(node->stride()->h()); cloned->stride()->w(node->stride()->w()); + cloned->fusedActivationFunction(node->fusedActivationFunction()); } return cloned; } diff --git a/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp index 29a656c..e9ac6e6 100644 --- a/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp +++ b/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp @@ -32,6 +32,7 @@ TEST(CloneNodeTest, clone_TransposeConv) auto cloned_trconv = dynamic_cast(cloned); ASSERT_NE(nullptr, cloned_trconv); ASSERT_EQ(node_trconv->padding(), cloned_trconv->padding()); + ASSERT_EQ(node_trconv->fusedActivationFunction(), cloned_trconv->fusedActivationFunction()); } TEST(CloneNodeTest, clone_TransposeConv_padding_NEG) @@ -44,3 +45,14 @@ TEST(CloneNodeTest, clone_TransposeConv_padding_NEG) auto cloned = luci::clone_node(node_trconv, gc.get()); ASSERT_EQ(nullptr, cloned); } + +TEST(CloneNodeTest, clone_TransposeConv_fAF_NEG) +{ + auto g = loco::make_graph(); + auto node_trconv = g->nodes()->create(); + node_trconv->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_trconv, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp index 77135cc..bdd2773 100644 --- a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp +++ b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp @@ -43,29 +43,33 @@ const int kMaxDim = 5; const loco::DataType S32 = loco::DataType::S32; -using int8 = int8_t; -using int16 = int16_t; - struct StridedSliceParams { - int8 start_indices_count = 0; - int16 start_indices[kMaxDim]; - int8 stop_indices_count = 0; - int16 stop_indices[kMaxDim]; - int8 strides_count = 0; - int16 strides[kMaxDim]; - - int16 begin_mask = 0; - int16 ellipsis_mask = 0; - int16 end_mask = 0; - int16 new_axis_mask = 0; - int16 shrink_axis_mask = 0; + int8_t start_indices_count = 0; + int32_t start_indices[kMaxDim]; + int8_t stop_indices_count = 0; + int32_t stop_indices[kMaxDim]; + int8_t strides_count = 0; + int32_t strides[kMaxDim]; + + int16_t begin_mask = 0; + int16_t ellipsis_mask = 0; + int16_t end_mask = 0; + int16_t new_axis_mask = 0; + int16_t shrink_axis_mask = 0; }; struct StridedSliceContext { StridedSliceContext(const luci::CircleStridedSlice *node) { + // check overflow issues + assert(static_cast(node->begin_mask()) == node->begin_mask()); + assert(static_cast(node->ellipsis_mask()) == node->ellipsis_mask()); + assert(static_cast(node->end_mask()) == node->end_mask()); + assert(static_cast(node->new_axis_mask()) == node->new_axis_mask()); + assert(static_cast(node->shrink_axis_mask()) == node->shrink_axis_mask()); + params.begin_mask = node->begin_mask(); params.ellipsis_mask = node->ellipsis_mask(); params.end_mask = node->end_mask(); @@ -88,7 +92,7 @@ struct StridedSliceContext // Equivalent input shape after adding axis according to new_axis_mask. loco::TensorShape effective_input_shape; - uint32_t input_dims = 0; + int64_t input_dims = 0; }; // Use until std::clamp() is available from C++17. @@ -105,19 +109,19 @@ inline int Clamp(const int32_t v, const int32_t lo, const int32_t hi) // Return the index for the first element along that axis. This index will be a // positive integer between [0, axis_size - 1] that can be used to index // directly into the data. -inline int32_t StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, - uint32_t axis) +inline int64_t StartForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, + int64_t axis) { const auto begin_mask = params.begin_mask; const auto *start_indices = params.start_indices; const auto *strides = params.strides; - const int32_t axis_size = static_cast(input_shape.dim(axis).value()); + const int64_t axis_size = static_cast(input_shape.dim(axis).value()); if (axis_size == 0) { return 0; } // Begin with the specified index. - int32_t start = start_indices[axis]; + int64_t start = start_indices[axis]; // begin_mask override if (begin_mask & (1 << axis)) @@ -162,14 +166,14 @@ inline int32_t StartForAxis(const StridedSliceParams ¶ms, const loco::Tensor // element. ie. So if you were iterating through all elements of a 1D array of // size 4, this function would return 4 as the stop, because it is one past the // "real" indices of 0, 1, 2 & 3. -inline int32_t StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, - int32_t axis, int32_t start_for_axis) +inline int64_t StopForAxis(const StridedSliceParams ¶ms, const loco::TensorShape &input_shape, + int64_t axis, int64_t start_for_axis) { const auto end_mask = params.end_mask; const auto shrink_axis_mask = params.shrink_axis_mask; const auto *stop_indices = params.stop_indices; const auto *strides = params.strides; - const int32_t axis_size = static_cast(input_shape.dim(axis).value()); + const int64_t axis_size = static_cast(input_shape.dim(axis).value()); if (axis_size == 0) { return 0; @@ -177,7 +181,7 @@ inline int32_t StopForAxis(const StridedSliceParams ¶ms, const loco::TensorS // Begin with the specified index const bool shrink_axis = shrink_axis_mask & (1 << axis); - int32_t stop = stop_indices[axis]; + int64_t stop = stop_indices[axis]; // When shrinking an axis, the end position does not matter (and can be // incorrect when negative indexing is used, see Issue #19260). Always use @@ -241,9 +245,9 @@ StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context) // Count indexes where the new_axis_mask is set but the ellipsis_mask is not. loco::TensorShape begin_shape = luci::shape_get(op_context->begin).as(); - const uint32_t begin_count = begin_shape.dim(0).value(); - uint32_t num_add_axis = 0; - for (uint32_t i = 0; i < begin_count; ++i) + const int64_t begin_count = static_cast(begin_shape.dim(0).value()); + int64_t num_add_axis = 0; + for (int64_t i = 0; i < begin_count; ++i) { if (!((1 << i) & op_context->params.ellipsis_mask) && ((1 << i) & op_context->params.new_axis_mask)) @@ -253,21 +257,21 @@ StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context) } // Calculate the dims of input after adding new axises. - const uint32_t effective_dims = op_context->input_dims + num_add_axis; + const int64_t effective_dims = op_context->input_dims + num_add_axis; // If begin, end and strides are not fully provided, it means Ellipsis should // be expanded to multiple dimensions (Ex: for spec [Ellipsis, 2] on a 3D // input, the Ellipsis should be applied for the first 2 dimensions). Besides, // If the new_axis_mask and the ellipsis_mask are set at the same index, the // new_axis_mask will have no effect. - int32_t effective_ellipsis_mask = 0, effective_new_axis_mask = 0; - uint32_t ellipsis_start_idx = effective_dims, expanded_ellipsis = 0; - for (uint32_t i = 0; i < effective_dims;) + int64_t effective_ellipsis_mask = 0, effective_new_axis_mask = 0; + int64_t ellipsis_start_idx = effective_dims, expanded_ellipsis = 0; + for (int64_t i = 0; i < effective_dims;) { if ((1 << i) & op_context->params.ellipsis_mask) { ellipsis_start_idx = i; - uint32_t ellipsis_end_idx = + int64_t ellipsis_end_idx = std::max(i + 1, std::min(i + 1 + num_add_axis + op_context->input_dims - begin_count, effective_dims)); expanded_ellipsis = ellipsis_end_idx - ellipsis_start_idx - 1; @@ -289,15 +293,16 @@ StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context) // Calculate effective_input_shape and its corresponding begin, end, strides. loco::TensorShape input_shape = luci::shape_get(op_context->input).as(); - uint32_t added_ellipsis = 0, added_axises = 0; + int64_t added_ellipsis = 0, added_axises = 0; op_context->effective_input_shape.rank(effective_dims); - for (uint32_t i = 0; i < effective_dims; ++i) + for (int64_t i = 0; i < effective_dims; ++i) { if ((1 << i) & effective_ellipsis_mask) { // If ellipsis_mask, set the begin_mask and end_mask at that index. - added_ellipsis = std::max(0u, i - ellipsis_start_idx); + added_ellipsis = std::max(int64_t(0), i - ellipsis_start_idx); + assert(i < 16); op_params.begin_mask |= (1 << i); op_params.end_mask |= (1 << i); op_params.strides[i] = 1; @@ -318,31 +323,39 @@ StridedSliceParams BuildStridedSliceParams(StridedSliceContext *op_context) op_params.start_indices[i] = 0; op_params.stop_indices[i] = 0; op_params.strides[i] = 1; + assert(i < 16); op_params.begin_mask |= (1 << i); op_params.end_mask |= (1 << i); op_context->effective_input_shape.dim(i) = input_shape.dim(i - added_axises); } else { - const uint32_t orig_idx = i - added_ellipsis; + const int64_t orig_idx = i - added_ellipsis; op_params.start_indices[i] = op_context->begin->at(orig_idx); op_params.stop_indices[i] = op_context->end->at(orig_idx); op_params.strides[i] = op_context->strides->at(orig_idx); if (op_context->params.begin_mask & (1 << orig_idx)) { + assert(i < 16); op_params.begin_mask |= (1 << i); } if (op_context->params.end_mask & (1 << orig_idx)) { + assert(i < 16); op_params.end_mask |= (1 << i); } if (op_context->params.shrink_axis_mask & (1 << orig_idx)) { + assert(i < 16); op_params.shrink_axis_mask |= (1 << i); } op_context->effective_input_shape.dim(i) = input_shape.dim(i - added_axises); } } + + // make sure no overflow + assert(static_cast(effective_dims) == static_cast(effective_dims)); + op_params.start_indices_count = effective_dims; op_params.stop_indices_count = effective_dims; op_params.strides_count = effective_dims; @@ -386,15 +399,15 @@ loco::TensorShape infer_output_shape(const CircleStridedSlice *node) StridedSliceContext op_context(node); auto op_params = BuildStridedSliceParams(&op_context); auto effective_input_shape = op_context.effective_input_shape; - std::vector output_shape_vector; + std::vector output_shape_vector; for (int32_t idx = effective_input_shape.rank() - 1; idx >= 0; --idx) { int32_t stride = op_params.strides[idx]; LUCI_ASSERT(stride != 0, "stride value has to be non-zero"); - int32_t begin = StartForAxis(op_params, effective_input_shape, idx); - int32_t end = StopForAxis(op_params, effective_input_shape, idx, begin); + int64_t begin = StartForAxis(op_params, effective_input_shape, idx); + int64_t end = StopForAxis(op_params, effective_input_shape, idx, begin); // When shrinking an axis, the end position does not matter (and can be // incorrect when negative indexing is used, see Issue #19260). Always use @@ -407,7 +420,7 @@ loco::TensorShape infer_output_shape(const CircleStridedSlice *node) } // This is valid for both positive and negative strides - int32_t dim_shape = std::ceil((end - begin) / static_cast(stride)); + int64_t dim_shape = std::ceil((end - begin) / static_cast(stride)); dim_shape = dim_shape < 0 ? 0 : dim_shape; if (!shrink_axis) { @@ -419,8 +432,10 @@ loco::TensorShape infer_output_shape(const CircleStridedSlice *node) output_shape.rank(shape_size); for (uint32_t idx = 0; idx < shape_size; ++idx) { + int64_t dim = output_shape_vector.at(shape_size - 1u - idx); + LUCI_ASSERT(0 <= dim && dim < 0xfffffffL, "Dimension size exceeds limit"); // reverse copy - output_shape.dim(idx) = output_shape_vector.at(shape_size - 1u - idx); + output_shape.dim(idx) = static_cast(dim); } return output_shape; diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp index 2f78e05..3f6f9c0 100644 --- a/compiler/luci/service/src/Validate.cpp +++ b/compiler/luci/service/src/Validate.cpp @@ -82,6 +82,65 @@ luci::CircleOutput *find_node(std::vector nodes, loco::GraphOutput return nullptr; } +// TODO Reduce duplicate with validate_shape_dtype +bool validate_shape(loco::Graph *g) +{ + LOGGER(l); + + auto output_nodes = loco::output_nodes(g); + + auto count = g->outputs()->size(); + for (uint32_t out = 0; out < count; ++out) + { + auto graph_out = g->outputs()->at(out); + auto out_index = graph_out->index(); + + auto circle_output = find_node(output_nodes, out_index); + assert(circle_output != nullptr); + assert(circle_output->from() != nullptr); + auto circle_node = loco::must_cast(circle_output->from()); + + // Shape validation for CircleOutputExclude is not needed + if (dynamic_cast(circle_node)) + continue; + + assert(circle_node->shape_status() != luci::ShapeStatus::UNDEFINED); + + // check if output node shape is same as graph output shape + auto go_tensor_shape = graph_out->shape(); + assert(go_tensor_shape); + + // NOTE Even if shape of graph output is [] (which means "shape inference was impossible") + // but shape of CircleNode is not, it can be valid case because shape inference + // algorithm of CircleNode may be upgraded than before. The opposite is possible either. + // If such cases are appeared, following validation code should be fixed. + bool is_shape_valid = (circle_node->rank() == go_tensor_shape->rank()); + for (uint32_t i = 0; is_shape_valid && i < circle_node->rank(); ++i) + { + if (!circle_node->dim(i).known() || !go_tensor_shape->dim(i).known()) + { + // If at least one of two dimensions is unknown, + // the unknown dimension can accept any value. + INFO(l) << "Unknown dimension is matched with known dimension" << std::endl; + } + else if (circle_node->dim(i).value() != go_tensor_shape->dim(i).value()) + { + is_shape_valid = false; + } + } + + if (is_shape_valid == false) + { + INFO(l) << "[luci] Shape for output #" << out_index << " not same " << std::endl; + INFO(l) << "[luci] " << circle_node->name() << " " << circle_node << " vs " + << *go_tensor_shape << std::endl; + return false; + } + } + + return true; +} + bool validate_shape_dtype(loco::Graph *g) { LOGGER(l); @@ -249,6 +308,17 @@ public: namespace luci { +bool validate_shape(loco::Graph *g) +{ + if (!loco::valid(g)) + return false; + + if (!::validate_shape(g)) + return false; + + return true; +} + bool validate(loco::Graph *g) { if (!loco::valid(g)) @@ -358,4 +428,26 @@ bool validate(luci::Module *module) return true; } +bool validate_shape(luci::Module *module) +{ + LOGGER(l); + + INFO(l) << "--- validate shape of Module -----------------------------------"; + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + + INFO(l) << luci::fmt(graph) << std::endl; + + if (!validate_shape(graph)) + { + std::cerr << "ERROR: Invalid circle model" << std::endl; + return false; + } + } + + return true; +} + } // namespace luci diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst index 8291f20..a88661d 100644 --- a/compiler/luci/tests/test.lst +++ b/compiler/luci/tests/test.lst @@ -69,8 +69,10 @@ addread(FullyConnected_002) addread(FullyConnected_U8_000) addread(Gather_000) addread(GatherNd_000) +addread(Gelu_000) addread(Greater_000) addread(GreaterEqual_000) +addread(HardSwish_000) addread(If_000) addread(If_001) addread(L2Normalize_000) @@ -297,8 +299,10 @@ addwrite(FullyConnected_002) addwrite(FullyConnected_U8_000) addwrite(Gather_000) addwrite(GatherNd_000) +addwrite(Gelu_000) addwrite(Greater_000) addwrite(GreaterEqual_000) +addwrite(HardSwish_000) addwrite(If_000) addwrite(If_001) addwrite(L2Normalize_000) diff --git a/compiler/mio-circle05/CMakeLists.txt b/compiler/mio-circle05/CMakeLists.txt new file mode 100644 index 0000000..dfd359e --- /dev/null +++ b/compiler/mio-circle05/CMakeLists.txt @@ -0,0 +1,52 @@ +nnas_find_package(FlatBuffers EXACT 2.0 QUIET) + +if(NOT FlatBuffers_FOUND) + message(STATUS "mio-circle05 skip: FlatBuffers 2.0 NOT FOUND") + return() +endif(NOT FlatBuffers_FOUND) + +message(STATUS "Build mio-circle05: TRUE") + +# TODO Find a better way +# TODO use nnpackage +# set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema/circle_schema.fbs") +set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/res/CircleSchema/0.5/circle_schema.fbs") + +# NOTE Copy circle_schema.fbs as schema.fbs to generate "schema_generated.fbs" instead of "circle_schema_generated.fbs" +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" +) + +FlatBuffers_Target(mio_circle05 + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/circle" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +# This example shows how to use "mio-circle05" library +add_executable(mio_circle05_example example.cpp) +target_link_libraries(mio_circle05_example mio_circle05) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(mio_circle05_helper STATIC ${SOURCES}) +set_target_properties(mio_circle05_helper PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(mio_circle05_helper PRIVATE src) +target_include_directories(mio_circle05_helper PUBLIC include) +target_link_libraries(mio_circle05_helper mio_circle05) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(mio_circle05_helper_test ${TESTS}) +target_include_directories(mio_circle05_helper_test PRIVATE src) +target_link_libraries(mio_circle05_helper_test mio_circle05) +target_link_libraries(mio_circle05_helper_test mio_circle05_helper) diff --git a/compiler/mio-circle05/README.md b/compiler/mio-circle05/README.md new file mode 100644 index 0000000..9296436 --- /dev/null +++ b/compiler/mio-circle05/README.md @@ -0,0 +1,3 @@ +# mio-circle05 + +Let's make it easy to read and write Circle models. diff --git a/compiler/mio-circle05/example.cpp b/compiler/mio-circle05/example.cpp new file mode 100644 index 0000000..31cd3fb --- /dev/null +++ b/compiler/mio-circle05/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 example shows how to include and use "mio-circle05" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!circle::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mio-circle05/include/mio_circle/Helper.h b/compiler/mio-circle05/include/mio_circle/Helper.h new file mode 100644 index 0000000..933f385 --- /dev/null +++ b/compiler/mio-circle05/include/mio_circle/Helper.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIO_CIRCLE05_HELPER_H__ +#define __MIO_CIRCLE05_HELPER_H__ + +#include + +#include + +namespace mio +{ +namespace circle +{ + +::circle::BuiltinOperator builtin_code_neutral(const ::circle::OperatorCode *opcode); +bool is_valid(const ::circle::OperatorCode *opcode); +bool is_custom(const ::circle::OperatorCode *opcode); +std::string opcode_name(const ::circle::OperatorCode *opcode); +const char *tensor_type(const ::circle::Tensor *tensor); +const char *tensor_name(const ::circle::Tensor *tensor); + +template std::vector as_index_vector(const flatbuffers::Vector *flat_array) +{ + if (flat_array == nullptr) + { + throw std::runtime_error("flat array is nullptr"); + } + + std::vector ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + +} // namespace circle +} // namespace mio + +#endif // __MIO_CIRCLE05_HELPER_H__ diff --git a/compiler/mio-circle05/include/mio_circle/Reader.h b/compiler/mio-circle05/include/mio_circle/Reader.h new file mode 100644 index 0000000..d751a30 --- /dev/null +++ b/compiler/mio-circle05/include/mio_circle/Reader.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIO_CIRCLE05_READER_H__ +#define __MIO_CIRCLE05_READER_H__ + +#include + +#include +#include +#include + +// NOTE Reader class originated from circledump and for circle-tensordump +// where this class has more work to be done for stability +// as the tools are for developers not customores. + +namespace mio +{ +namespace circle +{ + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class Reader +{ +private: + using CircleSubGraphs_t = flatbuffers::Vector>; + using CircleBuffers_t = flatbuffers::Vector>; + using CircleTensors_t = flatbuffers::Vector>; + using CircleOperators_t = flatbuffers::Vector>; + using CircleMetadata_t = flatbuffers::Vector>; + using CircleSignatureDef_t = flatbuffers::Vector>; + +public: + Reader(const ::circle::Model *model); + + Reader() = delete; + +public: + uint32_t version() const { return _version; } + + const std::vector &opcodes() { return _op_codes; } + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector &inputs() const { return _inputs; } + const std::vector &outputs() const { return _outputs; } + const ::circle::DataFormat &data_format() const { return _data_format; } + const CircleMetadata_t *metadata() const { return _metadata; } + const CircleSignatureDef_t *signature_defs() const { return _signature_defs; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + ::circle::BuiltinOperator builtin_code(const ::circle::Operator *op) const; + std::string opcode_name(const ::circle::Operator *op) const; + std::vector outputs(const ::circle::Operator *op) const; + std::string tensor_name(const ::circle::Tensor *tensor) const; + std::string tensor_dtype(const ::circle::Tensor *tensor) const; + +public: + bool select_subgraph(uint32_t subgraph); + const std::string &subgraph_name(void) const { return _subgraph_name; } + uint32_t subgraph_index(void) const { return _subgraph_index; } + +private: + uint32_t _version; + + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + const CircleMetadata_t *_metadata{nullptr}; + const CircleSignatureDef_t *_signature_defs{nullptr}; + + uint32_t _subgraph_index = 0; + std::string _subgraph_name; + std::vector _op_codes; + std::vector _inputs; + std::vector _outputs; + ::circle::DataFormat _data_format = ::circle::DataFormat::DataFormat_CHANNELS_FIRST; +}; + +} // namespace circle +} // namespace mio + +#endif // __MIO_CIRCLE05_READER_H__ diff --git a/compiler/mio-circle05/src/Helper.cpp b/compiler/mio-circle05/src/Helper.cpp new file mode 100644 index 0000000..bbfa204 --- /dev/null +++ b/compiler/mio-circle05/src/Helper.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Helper.h" + +#include +#include + +namespace mio +{ +namespace circle +{ + +/** + * This will provide v3/v3a/v3b format neutral BuiltinOperator + * NOTE circle has minus value opcode (252~254 as uint8_t) + * we cannot use std::max() like tflite as deprecated_builtin_code can be + * minus and builtin_code being 0 for v0.3 files. + */ +::circle::BuiltinOperator builtin_code_neutral(const ::circle::OperatorCode *opcode) +{ + assert(opcode != nullptr); + if (opcode->deprecated_builtin_code() == 127) + { + assert(opcode->builtin_code() >= 127); + return opcode->builtin_code(); + } + // There was no 255(-1) value in v0.3 + assert(opcode->deprecated_builtin_code() != -1); + return static_cast<::circle::BuiltinOperator>(opcode->deprecated_builtin_code()); +} + +bool is_valid(const ::circle::OperatorCode *opcode) +{ + // Valid Range : BuiltinOperator_MIN <= deprecated_builtin_code <= 127 + const int8_t deprecated_builtin_code = opcode->deprecated_builtin_code(); + if (deprecated_builtin_code < ::circle::BuiltinOperator_MIN) + return false; + // There was no 255(-1) value in v0.3 + if (deprecated_builtin_code == -1) + return false; + + const ::circle::BuiltinOperator builtin_code = opcode->builtin_code(); + if (!(::circle::BuiltinOperator_MIN <= builtin_code && + builtin_code <= ::circle::BuiltinOperator_MAX)) + return false; + + return true; +} + +bool is_custom(const ::circle::OperatorCode *opcode) +{ + ::circle::BuiltinOperator code = builtin_code_neutral(opcode); + return (code == ::circle::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const ::circle::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + std::string custom_op = "CUSTOM("; + custom_op += opcode->custom_code()->c_str(); + custom_op += ")"; + return custom_op; + } + + ::circle::BuiltinOperator code = builtin_code_neutral(opcode); + return ::circle::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const ::circle::Tensor *tensor) +{ + return ::circle::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const ::circle::Tensor *tensor) +{ + if (tensor->name() == nullptr || std::string(tensor->name()->c_str()).empty()) + return "(noname)"; + + return tensor->name()->c_str(); +} + +} // namespace circle +} // namespace mio diff --git a/compiler/mio-circle05/src/Helper.test.cpp b/compiler/mio-circle05/src/Helper.test.cpp new file mode 100644 index 0000000..be63688 --- /dev/null +++ b/compiler/mio-circle05/src/Helper.test.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Helper.h" + +#include +#include + +#include + +class mio_circle05_helper_test : public ::testing::Test +{ +protected: + void initialization_finish(void) + { + _fbb.Finish(circle::CreateModelDirect(_fbb, 0, &_opcodes_vec)); + } + +protected: + void add_operator_code(int8_t deprecated_builtin_code, const char *custom_code, + circle::BuiltinOperator builtin_code) + { + _opcodes_vec.push_back(circle::CreateOperatorCodeDirect( + _fbb, deprecated_builtin_code, custom_code, 1 /* version */, builtin_code)); + } + + const circle::OperatorCode *get_operator_code(uint8_t idx) + { + return circle::GetModel(_fbb.GetBufferPointer())->operator_codes()->Get(idx); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + std::vector> _opcodes_vec; +}; + +TEST_F(mio_circle05_helper_test, v05) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_custom_old) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_NEG) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_under127) +{ + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", circle::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_under127_NEG) +{ + // BuiltinOperator_CONV_2D = 3 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_custom) +{ + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", circle::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_custom_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "custom", circle::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_over127) +{ + // BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES = 127 + // BuiltinOperator_CUMSUM = 128 + add_operator_code(127, "", circle::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUMSUM); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle05_helper_test, v05_over127_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} diff --git a/compiler/mio-circle05/src/Reader.cpp b/compiler/mio-circle05/src/Reader.cpp new file mode 100644 index 0000000..0ee22db --- /dev/null +++ b/compiler/mio-circle05/src/Reader.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Reader.h" +#include "mio_circle/Helper.h" + +#include +#include + +namespace mio +{ +namespace circle +{ + +Reader::Reader(const ::circle::Model *model) +{ + if (model == nullptr) + { + throw std::runtime_error("Invalid model"); + } + + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + _metadata = model->metadata(); + _signature_defs = model->signature_defs(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +{ + if (buff_data != nullptr) + { + *buff_data = nullptr; + } + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + if (buff_data != nullptr) + { + *buff_data = reinterpret_cast(array->data()); + } + return size; + } + } + } + + return 0; +} + +::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + return mio::circle::builtin_code_neutral(opcode); +} + +std::string Reader::opcode_name(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + if (!mio::circle::is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return mio::circle::opcode_name(opcode); +} + +std::vector Reader::outputs(const ::circle::Operator *op) const +{ + return as_index_vector(op->outputs()); +} + +std::string Reader::tensor_name(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_name(tensor); +} + +std::string Reader::tensor_dtype(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_type(tensor); +} + +bool Reader::select_subgraph(uint32_t sgindex) +{ + _subgraph_index = sgindex; + _tensors = nullptr; + _operators = nullptr; + + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const ::circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + auto name = subgraph->name(); + _subgraph_name = name ? name->c_str() : "(noname)"; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + _data_format = subgraph->data_format(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +} // namespace circle +} // namespace mio diff --git a/compiler/mio-circle05/src/Reader.test.cpp b/compiler/mio-circle05/src/Reader.test.cpp new file mode 100644 index 0000000..0c60999 --- /dev/null +++ b/compiler/mio-circle05/src/Reader.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Reader.h" + +#include +#include + +class mio_circle05_reader_test : public ::testing::Test +{ +protected: + void initialization_emty(void) + { + _model = circle::CreateModelDirect(_fbb, 0, &_opcodes_vec); + circle::FinishModelBuffer(_fbb, _model); + } + + const circle::Model *circleModel(void) + { + auto ptr = _fbb.GetBufferPointer(); + return circle::GetModel(ptr); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + flatbuffers::Offset _model; + std::vector> _opcodes_vec; +}; + +TEST_F(mio_circle05_reader_test, null_Model_NEG) +{ + EXPECT_THROW(mio::circle::Reader reader(nullptr), std::runtime_error); +} + +TEST_F(mio_circle05_reader_test, empty_Model) +{ + initialization_emty(); + + const circle::Model *model = circleModel(); + EXPECT_NE(nullptr, model); + + mio::circle::Reader reader(model); + + SUCCEED(); +} + +// TODO add more tests diff --git a/compiler/mio-circle06/CMakeLists.txt b/compiler/mio-circle06/CMakeLists.txt new file mode 100644 index 0000000..2ccd805 --- /dev/null +++ b/compiler/mio-circle06/CMakeLists.txt @@ -0,0 +1,52 @@ +nnas_find_package(FlatBuffers EXACT 2.0 QUIET) + +if(NOT FlatBuffers_FOUND) + message(STATUS "mio-circle06 skip: FlatBuffers 2.0 NOT FOUND") + return() +endif(NOT FlatBuffers_FOUND) + +message(STATUS "Build mio-circle06: TRUE") + +# TODO Find a better way +# TODO use nnpackage +# set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema/circle_schema.fbs") +set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/res/CircleSchema/0.6/circle_schema.fbs") + +# NOTE Copy circle_schema.fbs as schema.fbs to generate "schema_generated.fbs" instead of "circle_schema_generated.fbs" +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" +) + +FlatBuffers_Target(mio_circle06 + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/circle" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +# This example shows how to use "mio-circle06" library +add_executable(mio_circle06_example example.cpp) +target_link_libraries(mio_circle06_example mio_circle06) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(mio_circle06_helper STATIC ${SOURCES}) +set_target_properties(mio_circle06_helper PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(mio_circle06_helper PRIVATE src) +target_include_directories(mio_circle06_helper PUBLIC include) +target_link_libraries(mio_circle06_helper mio_circle06) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(mio_circle06_helper_test ${TESTS}) +target_include_directories(mio_circle06_helper_test PRIVATE src) +target_link_libraries(mio_circle06_helper_test mio_circle06) +target_link_libraries(mio_circle06_helper_test mio_circle06_helper) diff --git a/compiler/mio-circle06/README.md b/compiler/mio-circle06/README.md new file mode 100644 index 0000000..c842964 --- /dev/null +++ b/compiler/mio-circle06/README.md @@ -0,0 +1,3 @@ +# mio-circle06 + +Let's make it easy to read and write Circle models. diff --git a/compiler/mio-circle06/example.cpp b/compiler/mio-circle06/example.cpp new file mode 100644 index 0000000..e99e454 --- /dev/null +++ b/compiler/mio-circle06/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 example shows how to include and use "mio-circle06" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!circle::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mio-circle06/include/mio_circle/Helper.h b/compiler/mio-circle06/include/mio_circle/Helper.h new file mode 100644 index 0000000..55cab58 --- /dev/null +++ b/compiler/mio-circle06/include/mio_circle/Helper.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIO_CIRCLE06_HELPER_H__ +#define __MIO_CIRCLE06_HELPER_H__ + +#include + +#include + +namespace mio +{ +namespace circle +{ + +::circle::BuiltinOperator builtin_code_neutral(const ::circle::OperatorCode *opcode); +bool is_valid(const ::circle::OperatorCode *opcode); +bool is_custom(const ::circle::OperatorCode *opcode); +std::string opcode_name(const ::circle::OperatorCode *opcode); +const char *tensor_type(const ::circle::Tensor *tensor); +const char *tensor_name(const ::circle::Tensor *tensor); + +template std::vector as_index_vector(const flatbuffers::Vector *flat_array) +{ + if (flat_array == nullptr) + { + throw std::runtime_error("flat array is nullptr"); + } + + std::vector ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + +} // namespace circle +} // namespace mio + +#endif // __MIO_CIRCLE06_HELPER_H__ diff --git a/compiler/mio-circle06/include/mio_circle/Reader.h b/compiler/mio-circle06/include/mio_circle/Reader.h new file mode 100644 index 0000000..3570614 --- /dev/null +++ b/compiler/mio-circle06/include/mio_circle/Reader.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIO_CIRCLE06_READER_H__ +#define __MIO_CIRCLE06_READER_H__ + +#include + +#include +#include +#include + +// NOTE Reader class originated from circledump and for circle-tensordump +// where this class has more work to be done for stability +// as the tools are for developers not customores. + +namespace mio +{ +namespace circle +{ + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class Reader +{ +private: + using CircleSubGraphs_t = flatbuffers::Vector>; + using CircleBuffers_t = flatbuffers::Vector>; + using CircleTensors_t = flatbuffers::Vector>; + using CircleOperators_t = flatbuffers::Vector>; + using CircleMetadata_t = flatbuffers::Vector>; + using CircleSignatureDef_t = flatbuffers::Vector>; + +public: + Reader(const ::circle::Model *model); + + Reader() = delete; + +public: + uint32_t version() const { return _version; } + + const std::vector &opcodes() { return _op_codes; } + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector &inputs() const { return _inputs; } + const std::vector &outputs() const { return _outputs; } + const ::circle::DataFormat &data_format() const { return _data_format; } + const CircleMetadata_t *metadata() const { return _metadata; } + const CircleSignatureDef_t *signature_defs() const { return _signature_defs; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data); + ::circle::BuiltinOperator builtin_code(const ::circle::Operator *op) const; + std::string opcode_name(const ::circle::Operator *op) const; + std::vector outputs(const ::circle::Operator *op) const; + std::string tensor_name(const ::circle::Tensor *tensor) const; + std::string tensor_dtype(const ::circle::Tensor *tensor) const; + +public: + bool select_subgraph(uint32_t subgraph); + const std::string &subgraph_name(void) const { return _subgraph_name; } + uint32_t subgraph_index(void) const { return _subgraph_index; } + +private: + uint32_t _version; + + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + const CircleMetadata_t *_metadata{nullptr}; + const CircleSignatureDef_t *_signature_defs{nullptr}; + + uint32_t _subgraph_index = 0; + std::string _subgraph_name; + std::vector _op_codes; + std::vector _inputs; + std::vector _outputs; + ::circle::DataFormat _data_format = ::circle::DataFormat::DataFormat_CHANNELS_FIRST; +}; + +} // namespace circle +} // namespace mio + +#endif // __MIO_CIRCLE06_READER_H__ diff --git a/compiler/mio-circle06/src/Helper.cpp b/compiler/mio-circle06/src/Helper.cpp new file mode 100644 index 0000000..bbfa204 --- /dev/null +++ b/compiler/mio-circle06/src/Helper.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Helper.h" + +#include +#include + +namespace mio +{ +namespace circle +{ + +/** + * This will provide v3/v3a/v3b format neutral BuiltinOperator + * NOTE circle has minus value opcode (252~254 as uint8_t) + * we cannot use std::max() like tflite as deprecated_builtin_code can be + * minus and builtin_code being 0 for v0.3 files. + */ +::circle::BuiltinOperator builtin_code_neutral(const ::circle::OperatorCode *opcode) +{ + assert(opcode != nullptr); + if (opcode->deprecated_builtin_code() == 127) + { + assert(opcode->builtin_code() >= 127); + return opcode->builtin_code(); + } + // There was no 255(-1) value in v0.3 + assert(opcode->deprecated_builtin_code() != -1); + return static_cast<::circle::BuiltinOperator>(opcode->deprecated_builtin_code()); +} + +bool is_valid(const ::circle::OperatorCode *opcode) +{ + // Valid Range : BuiltinOperator_MIN <= deprecated_builtin_code <= 127 + const int8_t deprecated_builtin_code = opcode->deprecated_builtin_code(); + if (deprecated_builtin_code < ::circle::BuiltinOperator_MIN) + return false; + // There was no 255(-1) value in v0.3 + if (deprecated_builtin_code == -1) + return false; + + const ::circle::BuiltinOperator builtin_code = opcode->builtin_code(); + if (!(::circle::BuiltinOperator_MIN <= builtin_code && + builtin_code <= ::circle::BuiltinOperator_MAX)) + return false; + + return true; +} + +bool is_custom(const ::circle::OperatorCode *opcode) +{ + ::circle::BuiltinOperator code = builtin_code_neutral(opcode); + return (code == ::circle::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const ::circle::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + std::string custom_op = "CUSTOM("; + custom_op += opcode->custom_code()->c_str(); + custom_op += ")"; + return custom_op; + } + + ::circle::BuiltinOperator code = builtin_code_neutral(opcode); + return ::circle::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const ::circle::Tensor *tensor) +{ + return ::circle::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const ::circle::Tensor *tensor) +{ + if (tensor->name() == nullptr || std::string(tensor->name()->c_str()).empty()) + return "(noname)"; + + return tensor->name()->c_str(); +} + +} // namespace circle +} // namespace mio diff --git a/compiler/mio-circle06/src/Helper.test.cpp b/compiler/mio-circle06/src/Helper.test.cpp new file mode 100644 index 0000000..9b158d1 --- /dev/null +++ b/compiler/mio-circle06/src/Helper.test.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Helper.h" + +#include +#include + +#include + +class mio_circle06_helper_test : public ::testing::Test +{ +protected: + void initialization_finish(void) + { + _fbb.Finish(circle::CreateModelDirect(_fbb, 0, &_opcodes_vec)); + } + +protected: + void add_operator_code(int8_t deprecated_builtin_code, const char *custom_code, + circle::BuiltinOperator builtin_code) + { + _opcodes_vec.push_back(circle::CreateOperatorCodeDirect( + _fbb, deprecated_builtin_code, custom_code, 1 /* version */, builtin_code)); + } + + const circle::OperatorCode *get_operator_code(uint8_t idx) + { + return circle::GetModel(_fbb.GetBufferPointer())->operator_codes()->Get(idx); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + std::vector> _opcodes_vec; +}; + +TEST_F(mio_circle06_helper_test, v06) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_custom_old) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_NEG) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_under127) +{ + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", circle::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_under127_NEG) +{ + // BuiltinOperator_CONV_2D = 3 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_custom) +{ + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", circle::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_custom_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "custom", circle::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_over127) +{ + // BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES = 127 + // BuiltinOperator_CUMSUM = 128 + add_operator_code(127, "", circle::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_TRUE(mio::circle::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::circle::builtin_code_neutral(get_operator_code(0)), + circle::BuiltinOperator_CUMSUM); + ASSERT_FALSE(mio::circle::is_custom(get_operator_code(0))); +} + +TEST_F(mio_circle06_helper_test, v06_over127_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", circle::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_FALSE(mio::circle::is_valid(get_operator_code(0))); +} diff --git a/compiler/mio-circle06/src/Reader.cpp b/compiler/mio-circle06/src/Reader.cpp new file mode 100644 index 0000000..0ee22db --- /dev/null +++ b/compiler/mio-circle06/src/Reader.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Reader.h" +#include "mio_circle/Helper.h" + +#include +#include + +namespace mio +{ +namespace circle +{ + +Reader::Reader(const ::circle::Model *model) +{ + if (model == nullptr) + { + throw std::runtime_error("Invalid model"); + } + + _version = model->version(); + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + _metadata = model->metadata(); + _signature_defs = model->signature_defs(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data) +{ + if (buff_data != nullptr) + { + *buff_data = nullptr; + } + + if (buf_idx == 0) + return 0; + + if (auto *buffer = (*_buffers)[buf_idx]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + if (buff_data != nullptr) + { + *buff_data = reinterpret_cast(array->data()); + } + return size; + } + } + } + + return 0; +} + +::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + return mio::circle::builtin_code_neutral(opcode); +} + +std::string Reader::opcode_name(const ::circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const ::circle::OperatorCode *opcode = _op_codes.at(index); + + if (!mio::circle::is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return mio::circle::opcode_name(opcode); +} + +std::vector Reader::outputs(const ::circle::Operator *op) const +{ + return as_index_vector(op->outputs()); +} + +std::string Reader::tensor_name(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_name(tensor); +} + +std::string Reader::tensor_dtype(const ::circle::Tensor *tensor) const +{ + return mio::circle::tensor_type(tensor); +} + +bool Reader::select_subgraph(uint32_t sgindex) +{ + _subgraph_index = sgindex; + _tensors = nullptr; + _operators = nullptr; + + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const ::circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + auto name = subgraph->name(); + _subgraph_name = name ? name->c_str() : "(noname)"; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + _data_format = subgraph->data_format(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +} // namespace circle +} // namespace mio diff --git a/compiler/mio-circle06/src/Reader.test.cpp b/compiler/mio-circle06/src/Reader.test.cpp new file mode 100644 index 0000000..668a8b1 --- /dev/null +++ b/compiler/mio-circle06/src/Reader.test.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_circle/Reader.h" + +#include +#include + +class mio_circle06_reader_test : public ::testing::Test +{ +protected: + void initialization_emty(void) + { + _model = circle::CreateModelDirect(_fbb, 0, &_opcodes_vec); + circle::FinishModelBuffer(_fbb, _model); + } + + const circle::Model *circleModel(void) + { + auto ptr = _fbb.GetBufferPointer(); + return circle::GetModel(ptr); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + flatbuffers::Offset _model; + std::vector> _opcodes_vec; +}; + +TEST_F(mio_circle06_reader_test, null_Model_NEG) +{ + EXPECT_THROW(mio::circle::Reader reader(nullptr), std::runtime_error); +} + +TEST_F(mio_circle06_reader_test, empty_Model) +{ + initialization_emty(); + + const circle::Model *model = circleModel(); + EXPECT_NE(nullptr, model); + + mio::circle::Reader reader(model); + + SUCCEED(); +} + +// TODO add more tests diff --git a/compiler/mio-tflite2121/CMakeLists.txt b/compiler/mio-tflite2121/CMakeLists.txt new file mode 100644 index 0000000..1ca8e75 --- /dev/null +++ b/compiler/mio-tflite2121/CMakeLists.txt @@ -0,0 +1,60 @@ +nnas_find_package(FlatBuffers EXACT 2.0 QUIET) + +if(NOT FlatBuffers_FOUND) + message(STATUS "Build mio-tflite2121: FAILED (missing Flatbuffers 2.0)") + return() +endif(NOT FlatBuffers_FOUND) + +nnas_find_package(TensorFlowSource EXACT 2.12.1 QUIET) + +if(NOT TensorFlowSource_FOUND) + message(STATUS "Build mio-tflite2121: FAILED (missing TensorFlowSource 2.12.1)") + return() +endif(NOT TensorFlowSource_FOUND) + +message(STATUS "Build mio-tflite2121: TRUE") +message(STATUS "Build mio-tflite2121: with ${TensorFlowSource_DIR}") + +set(SCHEMA_FILE "${TensorFlowSource_DIR}/tensorflow/lite/schema/schema.fbs") + +# NOTE Use copy of schema.fbs as to provide unified way for circle also +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" +) + +FlatBuffers_Target(mio_tflite2121 + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/mio/tflite" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" +) + +add_executable(mio_tflite2121_example example.cpp) +target_link_libraries(mio_tflite2121_example mio_tflite2121) + +# Temporay tflite validation tool to replace nnkit-tflite +# TODO provide full tflite validation with runtime/interpreter +add_executable(mio_tflite2121_validate example.cpp) +target_link_libraries(mio_tflite2121_validate mio_tflite2121) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(mio_tflite2121_helper STATIC ${SOURCES}) +target_include_directories(mio_tflite2121_helper PRIVATE src) +target_include_directories(mio_tflite2121_helper PUBLIC include) +target_link_libraries(mio_tflite2121_helper mio_tflite2121) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(mio_tflite2121_helper_test ${TESTS}) +target_include_directories(mio_tflite2121_helper_test PRIVATE src) +target_link_libraries(mio_tflite2121_helper_test mio_tflite2121) +target_link_libraries(mio_tflite2121_helper_test mio_tflite2121_helper) diff --git a/compiler/mio-tflite2121/README.md b/compiler/mio-tflite2121/README.md new file mode 100644 index 0000000..a922f30 --- /dev/null +++ b/compiler/mio-tflite2121/README.md @@ -0,0 +1,3 @@ +# mio-tflite2121 + +_mio-tflite2121_ provides a library to access TensorFlow lite model files with V2.12.1. diff --git a/compiler/mio-tflite2121/example.cpp b/compiler/mio-tflite2121/example.cpp new file mode 100644 index 0000000..54fe9e7 --- /dev/null +++ b/compiler/mio-tflite2121/example.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 example shows how to include and use "mio-tflite2121" +// +#include + +#include +#include +#include + +int main(int argc, char **argv) +{ + std::ifstream ifs(argv[1], std::ios_base::binary); + std::vector buf(std::istreambuf_iterator{ifs}, std::istreambuf_iterator{}); + + flatbuffers::Verifier verifier{reinterpret_cast(buf.data()), buf.size()}; + + if (!tflite::VerifyModelBuffer(verifier)) + { + std::cout << "Fail" << std::endl; + return 255; + } + + std::cout << "Pass" << std::endl; + return 0; +} diff --git a/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h b/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h new file mode 100644 index 0000000..f206260 --- /dev/null +++ b/compiler/mio-tflite2121/include/mio_tflite2121/Helper.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MIO_TFLITE2121_HELPER_H__ +#define __MIO_TFLITE2121_HELPER_H__ + +#include + +namespace mio +{ +namespace tflite +{ + +::tflite::BuiltinOperator builtin_code_neutral(const ::tflite::OperatorCode *opcode); +bool is_valid(const ::tflite::OperatorCode *opcode); +bool is_custom(const ::tflite::OperatorCode *opcode); +std::string opcode_name(const ::tflite::OperatorCode *opcode); +const char *tensor_type(const ::tflite::Tensor *tensor); +const char *tensor_name(const ::tflite::Tensor *tensor); + +} // namespace tflite +} // namespace mio + +#endif // __MIO_TFLITE2121_HELPER_H__ diff --git a/compiler/mio-tflite2121/src/Helper.cpp b/compiler/mio-tflite2121/src/Helper.cpp new file mode 100644 index 0000000..b0d1ba1 --- /dev/null +++ b/compiler/mio-tflite2121/src/Helper.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 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 "mio_tflite2121/Helper.h" + +#include + +namespace mio +{ +namespace tflite +{ + +/** + * This will provide v3/v3a format neutral BuiltinOperator + * + * This function referenced + * https://github.com/tensorflow/tensorflow/blob/7d12007d7800d3714a02e05059f3ea602d1aec78/tensorflow/lite/schema/schema_utils.cc + */ +::tflite::BuiltinOperator builtin_code_neutral(const ::tflite::OperatorCode *opcode) +{ + assert(opcode != nullptr); + return std::max(opcode->builtin_code(), + static_cast<::tflite::BuiltinOperator>(opcode->deprecated_builtin_code())); +} + +bool is_valid(const ::tflite::OperatorCode *opcode) +{ + // Valid Range : 0 <= deprecated_builtin_code <= 127 + const int8_t deprecated_builtin_code = opcode->deprecated_builtin_code(); + if (deprecated_builtin_code < 0) + return false; + + const ::tflite::BuiltinOperator builtin_code = opcode->builtin_code(); + if (!(::tflite::BuiltinOperator_MIN <= builtin_code && + builtin_code <= ::tflite::BuiltinOperator_MAX)) + return false; + + return true; +} + +bool is_custom(const ::tflite::OperatorCode *opcode) +{ + ::tflite::BuiltinOperator code = builtin_code_neutral(opcode); + return (code == ::tflite::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const ::tflite::OperatorCode *opcode) +{ + assert(opcode); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + std::string custom_op = "CUSTOM("; + custom_op += opcode->custom_code()->c_str(); + custom_op += ")"; + return custom_op; + } + + ::tflite::BuiltinOperator code = builtin_code_neutral(opcode); + return ::tflite::EnumNameBuiltinOperator(code); +} + +const char *tensor_type(const ::tflite::Tensor *tensor) +{ + return ::tflite::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const ::tflite::Tensor *tensor) +{ + static const char *kEmptyTensorName = "(noname)"; + + auto name = tensor->name(); + if (name) + return name->c_str(); + + return kEmptyTensorName; +} + +} // namespace tflite +} // namespace mio diff --git a/compiler/mio-tflite2121/src/Helper.test.cpp b/compiler/mio-tflite2121/src/Helper.test.cpp new file mode 100644 index 0000000..1527a49 --- /dev/null +++ b/compiler/mio-tflite2121/src/Helper.test.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mio_tflite2121/Helper.h" + +#include +#include + +#include + +class mio_tflite2121_helper_test : public ::testing::Test +{ +protected: + void initialization_finish(void) + { + _fbb.Finish(tflite::CreateModelDirect(_fbb, 0, &_opcodes_vec)); + } + +protected: + void add_operator_code(int8_t deprecated_builtin_code, const char *custom_code, + tflite::BuiltinOperator builtin_code) + { + _opcodes_vec.push_back(tflite::CreateOperatorCodeDirect( + _fbb, deprecated_builtin_code, custom_code, 1 /* version */, builtin_code)); + } + + const tflite::OperatorCode *get_operator_code(uint8_t idx) + { + return tflite::GetModel(_fbb.GetBufferPointer())->operator_codes()->Get(idx); + } + +private: + flatbuffers::FlatBufferBuilder _fbb; + std::vector> _opcodes_vec; +}; + +/** + * Extended 'builtin_code' is not in TFLite schema v3. + * + * Thus it is filled with 0(BuiltinOperator_ADD) in schame v3. Please refer to + * https://github.com/tensorflow/tensorflow/blob/1ab788fa8d08430be239ab970980b891ad7af494/tensorflow/lite/schema/schema_utils.cc#L28-L31 + */ +TEST_F(mio_tflite2121_helper_test, v3) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3_custom) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3_NEG) +{ + // BuiltinOperator_ADD = 0 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_ADD); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_under127) +{ + // BuiltinOperator_CONV_2D = 3 + add_operator_code(3, "", tflite::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CONV_2D); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_under127_NEG) +{ + // BuiltinOperator_CONV_2D = 3 + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_CONV_2D); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_custom) +{ + // BuiltinOperator_CUSTOM = 32 + add_operator_code(32, "custom", tflite::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUSTOM); + ASSERT_TRUE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_custom_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "custom", tflite::BuiltinOperator_CUSTOM); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_over127) +{ + // BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES = 127 + // BuiltinOperator_CUMSUM = 128 + add_operator_code(127, "", tflite::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_TRUE(mio::tflite::is_valid(get_operator_code(0))); + ASSERT_EQ(mio::tflite::builtin_code_neutral(get_operator_code(0)), + tflite::BuiltinOperator_CUMSUM); + ASSERT_FALSE(mio::tflite::is_custom(get_operator_code(0))); +} + +TEST_F(mio_tflite2121_helper_test, v3a_over127_NEG) +{ + // BuiltinOperator_CUMSUM = 128 + // deprecated_builtin_code cannot be negative value + add_operator_code(128, "", tflite::BuiltinOperator_CUMSUM); + initialization_finish(); + + ASSERT_FALSE(mio::tflite::is_valid(get_operator_code(0))); +} diff --git a/compiler/mir/unittests/ShapeRange.cpp b/compiler/mir/unittests/ShapeRange.cpp index 3797e3c..91b1be7 100644 --- a/compiler/mir/unittests/ShapeRange.cpp +++ b/compiler/mir/unittests/ShapeRange.cpp @@ -56,7 +56,7 @@ TEST_P(ShapeIteratorTest, ElementCount) std::vector test_data{ParamType{6, 1, 2, 3}, ParamType{16, 2, 2, 4}, ParamType{1, 1, 1, 1, 1, 1}, ParamType{5, 5, 1, 1, 1, 1, 1}}; -INSTANTIATE_TEST_CASE_P(SimpleInput, ShapeIteratorTest, ::testing::ValuesIn(test_data)); +INSTANTIATE_TEST_SUITE_P(SimpleInput, ShapeIteratorTest, ::testing::ValuesIn(test_data)); TEST(ShapeRange, Contains) { diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h index 0dd3150..33fc14a 100644 --- a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h +++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h @@ -15,7 +15,7 @@ */ #ifndef __MOCO_TF_SOFTMAX_CANONICALIZER_H__ -#define __MOCO_TF_SOFTMAx_CANONICALIZER_H__ +#define __MOCO_TF_SOFTMAX_CANONICALIZER_H__ #include "Transform.h" #include "SimpleNodeTransform.h" diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_header_types.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_header_types.def index 771329c..db46a1e 100644 --- a/compiler/nnc/backends/soft_backend/code_snippets/cpp_header_types.def +++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_header_types.def @@ -147,7 +147,7 @@ public: if (!t._managed) { if (_managed) - delete _data; + delete [] _data; _managed = false; _data = t._data; diff --git a/compiler/nnc/backends/soft_backend/code_snippets/eigen.def b/compiler/nnc/backends/soft_backend/code_snippets/eigen.def index b02f84b..b6547d5 100644 --- a/compiler/nnc/backends/soft_backend/code_snippets/eigen.def +++ b/compiler/nnc/backends/soft_backend/code_snippets/eigen.def @@ -13381,7 +13381,7 @@ struct palign_impl\ if (Offset!=0)\ first = Command(first, second, Offset);\ }\ -};\ +}; PALIGN_NEON(0,Packet2d,vextq_f64) PALIGN_NEON(1,Packet2d,vextq_f64) #undef PALIGN_NEON diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt index 7772b53..917bbda 100644 --- a/compiler/one-cmds/CMakeLists.txt +++ b/compiler/one-cmds/CMakeLists.txt @@ -36,13 +36,19 @@ set(ONE_COMMAND_FILES ) # TODO find better way for per-platform files -if(ONE_UBUNTU_CODENAME_JAMMY) - # NOTE copy one-prepare-venv.u2204 as build/../one-prepare-venv +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + # NOTE copy one-prepare-venv.aarch64 as build/../one-prepare-venv # and install build/../one-prepare-venv file - list(APPEND ONE_COMMAND_FILES one-prepare-venv.u2204) -else() - list(APPEND ONE_COMMAND_FILES one-prepare-venv) -endif() + list(APPEND ONE_COMMAND_FILES one-prepare-venv.aarch64) +else(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") + if(ONE_UBUNTU_CODENAME_BIONIC) + # NOTE copy one-prepare-venv.u1804 as build/../one-prepare-venv + # and install build/../one-prepare-venv file + list(APPEND ONE_COMMAND_FILES one-prepare-venv.u1804) + else(ONE_UBUNTU_CODENAME_BIONIC) + list(APPEND ONE_COMMAND_FILES one-prepare-venv) + endif(ONE_UBUNTU_CODENAME_BIONIC) +endif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64") # pytorch importer is an experimental feature, it is not used in default configuration if(ENABLE_ONE_IMPORT_PYTORCH) @@ -113,7 +119,8 @@ install(FILES ${MODEL2NNPKG} RENAME "model2nnpkg") # make python directory -set(ONE_PYTHON_FILES constant.py +set(ONE_PYTHON_FILES backends.py + constant.py export_constant.py make_cmd.py CfgRunner.py diff --git a/compiler/one-cmds/dummy-driver/CMakeLists.txt b/compiler/one-cmds/dummy-driver/CMakeLists.txt index 912798e..55ef96c 100644 --- a/compiler/one-cmds/dummy-driver/CMakeLists.txt +++ b/compiler/one-cmds/dummy-driver/CMakeLists.txt @@ -1,30 +1,42 @@ # dummy driver for interface test set(DUMMY_DRIVER_SRC src/dummy-compile.cpp) +set(DUMMY_V2_DRIVER_SRC src/dummyV2-compile.cpp) set(HELP_DRIVER_SRC src/help-compile.cpp) set(DUMMY_INFER_SRC src/dummy-infer.cpp) set(DUMMY_INFER_V2_SRC src/dummy-inferV2.cpp) set(HELP_INFER_SRC src/help-infer.cpp) set(DUMMY_PROFILE_SRC src/dummy-profile.cpp) +set(DUMMY_V2_PROFILE_SRC src/dummyV2-profile.cpp) +set(DUMMY_V3_PROFILE_SRC src/dummyV3-profile.cpp) set(HELP_PROFILE_SRC src/help-profile.cpp) set(DUMMY_ENV_SRC src/dummyEnv-compile.cpp) +set(DUMMY_ONNX_EXT src/dummy-onnx-ext.cpp) add_executable(dummy-compile ${DUMMY_DRIVER_SRC}) +add_executable(dummyV2-compile ${DUMMY_V2_DRIVER_SRC}) add_executable(help-compile ${HELP_DRIVER_SRC}) add_executable(dummy-infer ${DUMMY_INFER_SRC}) add_executable(dummy-inferV2 ${DUMMY_INFER_V2_SRC}) add_executable(help-infer ${HELP_INFER_SRC}) add_executable(dummy-profile ${DUMMY_PROFILE_SRC}) +add_executable(dummyV2-profile ${DUMMY_V2_PROFILE_SRC}) +add_executable(dummyV3-profile ${DUMMY_V3_PROFILE_SRC}) add_executable(help-profile ${HELP_PROFILE_SRC}) add_executable(dummyEnv-compile ${DUMMY_ENV_SRC}) +add_executable(dummy-onnx-ext ${DUMMY_ONNX_EXT}) set(DUMMY_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/dummy-compile") +set(DUMMY_V2_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/dummyV2-compile") set(HELP_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/help-compile") set(DUMMY_INFER "${CMAKE_CURRENT_BINARY_DIR}/dummy-infer") set(DUMMY_INFER_V2 "${CMAKE_CURRENT_BINARY_DIR}/dummy-inferV2") set(HELP_INFER "${CMAKE_CURRENT_BINARY_DIR}/help-infer") set(DUMMY_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/dummy-profile") +set(DUMMY_V2_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/dummyV2-profile") +set(DUMMY_V3_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/dummyV3-profile") set(HELP_PROFILE "${CMAKE_CURRENT_BINARY_DIR}/help-profile") set(DUMMY_ENV "${CMAKE_CURRENT_BINARY_DIR}/dummyEnv-compile") +set(DUMMY_ONNX_EXT "${CMAKE_CURRENT_BINARY_DIR}/dummy-onnx-ext") install(FILES ${DUMMY_DRIVER} PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE @@ -32,6 +44,12 @@ install(FILES ${DUMMY_DRIVER} WORLD_READ WORLD_EXECUTE DESTINATION test) +install(FILES ${DUMMY_V2_DRIVER} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + install(FILES ${HELP_DRIVER} PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE @@ -62,6 +80,18 @@ install(FILES ${DUMMY_PROFILE} WORLD_READ WORLD_EXECUTE DESTINATION test) +install(FILES ${DUMMY_V2_PROFILE} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + +install(FILES ${DUMMY_V3_PROFILE} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + install(FILES ${HELP_PROFILE} PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE @@ -73,3 +103,9 @@ install(FILES ${DUMMY_ENV} GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION test) + +install(FILES ${DUMMY_ONNX_EXT} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) diff --git a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.h b/compiler/one-cmds/dummy-driver/src/dummy-onnx-ext.cpp similarity index 59% rename from compiler/circle-mpqsolver/src/bisection/ErrorApproximator.h rename to compiler/one-cmds/dummy-driver/src/dummy-onnx-ext.cpp index efbfa82..845445b 100644 --- a/compiler/circle-mpqsolver/src/bisection/ErrorApproximator.h +++ b/compiler/one-cmds/dummy-driver/src/dummy-onnx-ext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,23 @@ * limitations under the License. */ -#ifndef __MPQSOLVER_BISECTION_ERROR_APPROXIMATOR_H__ -#define __MPQSOLVER_BISECTION_ERROR_APPROXIMATOR_H__ - -#include +/** + * dummy-onnx-ext only tests its interface rather than its functionality. + * + * ./dummy-onnx-ext [options] + * one-import-onnx-ext dummy output!!! + */ -#include +#include +#include +#include -namespace mpqsolver +int main(int argc, char **argv) { -namespace bisection -{ - -/** - * @brief approximate error introduced while quantizing node into Q8 - */ -float approximate(const luci::CircleNode *node); + (void)argc; + (void)argv; -} // namespace bisection -} // namespace mpqsolver + std::cout << "one-import-onnx-ext dummy output!!!" << std::endl; -#endif // __MPQSOLVER_BISECTION_ERROR_APPROXIMATOR_H__ + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/dummyV2-compile.cpp b/compiler/one-cmds/dummy-driver/src/dummyV2-compile.cpp new file mode 100644 index 0000000..bc7372e --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummyV2-compile.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * dummyV2-compile only tests its interface rather than its functionality. + * + * ./dummyV2-compile -o ${OUTPUT_NAME} ${INPUT_NAME} + * + * NOTE argv[3](INPUT_NAME) is not used here. + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 4) + return EXIT_FAILURE; + + std::string opt_o{"-O"}; + std::string argv_1{argv[1]}; + + if (opt_o != argv_1) + { + std::cout << "dummyV2-compile: Invalid option" << std::endl; + return EXIT_FAILURE; + } + + std::string output_name{argv[2]}; + std::ofstream outfile(output_name); + + outfile << "dummyV2-compile dummy output!!" << std::endl; + + outfile.close(); + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/dummyV2-profile.cpp b/compiler/one-cmds/dummy-driver/src/dummyV2-profile.cpp new file mode 100644 index 0000000..020f320 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummyV2-profile.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * dummyV2-profile only tests its interface rather than its functionality. + * + * ./dummyV2-profile ${INPUT_NAME} + * dummyV2-profile dummy output!!! + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::cout << "dummyV2-profile dummy output!!!" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/dummyV3-profile.cpp b/compiler/one-cmds/dummy-driver/src/dummyV3-profile.cpp new file mode 100644 index 0000000..d106446 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummyV3-profile.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * dummyV3-profile only tests its interface rather than its functionality. + * + * ./dummyV3-profile ${INPUT_TO_PRINT} + * dummyV3-profile with ${INPUT_TO_PRINT} + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::string input_to_print{argv[1]}; + + std::cout << "dummyV3-profile with " << input_to_print << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/one-codegen b/compiler/one-cmds/one-codegen index 5ccff0f..a956a63 100644 --- a/compiler/one-cmds/one-codegen +++ b/compiler/one-cmds/one-codegen @@ -27,53 +27,15 @@ import ntpath import os import sys import shutil +from types import SimpleNamespace +import onelib.backends as backends import onelib.utils as oneutils # TODO Find better way to suppress trackback on error sys.tracebacklimit = 0 -def _get_backends_list(): - """ - [one hierarchy] - one - ├── backends - ├── bin - ├── doc - ├── include - ├── lib - └── test - - The list where `one-codegen` finds its backends - - `bin` folder where `one-codegen` exists - - `backends` folder - - System path - - NOTE If there are backends of the same name in different places, - the closer to the top in the list, the higher the priority. - """ - dir_path = os.path.dirname(os.path.realpath(__file__)) - backend_set = set() - - # bin folder - files = [f for f in glob.glob(dir_path + '/*-compile')] - # backends folder - files += [ - f for f in glob.glob(dir_path + '/../backends/**/*-compile', recursive=True) - ] - # TODO find backends in `$PATH` - - backends_list = [] - for cand in files: - base = ntpath.basename(cand) - if not base in backend_set and os.path.isfile(cand) and os.access(cand, os.X_OK): - backend_set.add(base) - backends_list.append(cand) - - return backends_list - - def _get_parser(backends_list): codegen_usage = 'one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] [--] [COMMANDS FOR BACKEND]' parser = argparse.ArgumentParser( @@ -94,15 +56,47 @@ def _get_parser(backends_list): return parser -def _verify_arg(parser, args): +def _verify_arg(parser, args, cfg_args, backend_args, unknown_args): """verify given arguments""" + cmd_backend_exist = oneutils.is_valid_attr(args, 'backend') + cfg_backend_exist = oneutils.is_valid_attr(cfg_args, 'backend') + cfg_backends_exist = oneutils.is_valid_attr(cfg_args, 'backends') + # check if required arguments is given missing = [] - if not oneutils.is_valid_attr(args, 'backend'): + if not cmd_backend_exist and not cfg_backend_exist and not cfg_backends_exist: missing.append('-b/--backend') if len(missing): parser.error('the following arguments are required: ' + ' '.join(missing)) + if not oneutils.is_valid_attr(args, 'config'): + if not backend_args and not unknown_args: + parser.error('commands for the backend is missing.') + + if cfg_backend_exist and cfg_backends_exist: + parser.error( + '\'backend\' option and \'backends\' option cannot be used simultaneously.') + + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist and cfg_backend_exist: + if args.backend != cfg_args.backend: + parser.error('Not found the command of given backend') + + if cfg_backend_exist and not oneutils.is_valid_attr(cfg_args, 'command'): + parser.error('\'command\' key is missing in the configuration file.') + + if cfg_backends_exist: + cfg_backends = getattr(cfg_args, 'backends').split(',') + # check if commands of given backends exist + for b in cfg_backends: + if not oneutils.is_valid_attr(cfg_args, b): + parser.error('Not found the command for ' + b) + + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist: + if args.backend not in cfg_backends: + parser.error('Not found the command of given backend') + def _parse_arg(parser): codegen_args = [] @@ -134,36 +128,89 @@ def _parse_arg(parser): def main(): # get backend list - backends_list = _get_backends_list() + backends_list = backends.get_list('compile') # parse arguments parser = _get_parser(backends_list) args, backend_args, unknown_args = _parse_arg(parser) # parse configuration file - oneutils.parse_cfg(args.config, 'one-codegen', args) + cfg_args = SimpleNamespace() + oneutils.parse_cfg(args.config, 'one-codegen', cfg_args) + + # parse configuration file (args has arguments parsed from command line + cfg) + # oneutils.parse_cfg(args.config, 'one-codegen', args) # verify arguments - _verify_arg(parser, args) - - # make a command to run given backend driver - codegen_path = None - backend_base = getattr(args, 'backend') + '-compile' - for cand in backends_list: - if ntpath.basename(cand) == backend_base: - codegen_path = cand - if not codegen_path: - # Find backend from system path - codegen_path = shutil.which(backend_base) - - if not codegen_path: - raise FileNotFoundError(backend_base + ' not found') - codegen_cmd = [codegen_path] + backend_args + unknown_args - if oneutils.is_valid_attr(args, 'command'): - codegen_cmd += getattr(args, 'command').split() - - # run backend driver - oneutils.run(codegen_cmd, err_prefix=backend_base) + _verify_arg(parser, args, cfg_args, backend_args, unknown_args) + ''' + one-codegen defines its behavior for below cases. + + [1] one-codegen -h + [2] one-codegen -v + [3] one-codegen -C ${cfg} (backend, command key in cfg) + [4] one-codegen -C ${cfg} (backends key in cfg) + [5] one-codegen -b ${backend} ${command} + [6] one-codegen -b ${backend} -- ${command} + [7] one-codegen -b ${backend} -C {cfg} (backend, command key in cfg) + [8] one-codegen -b ${backend} -C {cfg} (backends key in cfg) (Only 'backend' is invoked, + even though cfg file has multiple backends) + [9] one-codegen -b ${backend} -C ${cfg} -- ${command} (backend, command key in cfg) + [10] one-codegen -b ${backend} -C ${cfg} -- ${command} (backends key in cfg) (Only 'backend' is invoked, + even though cfg file has multiple backends) + + All other cases are not allowed or an undefined behavior. + ''' + cmd_overwrite = False + if oneutils.is_valid_attr(args, 'config'): + # [9], [10] + if backend_args and not unknown_args: + given_backends = [args.backend] + cmd_overwrite = True + else: + # [7], [8] + if oneutils.is_valid_attr(args, 'backend'): + given_backends = [args.backend] + if oneutils.is_valid_attr(cfg_args, 'backend'): + assert (oneutils.is_valid_attr(cfg_args, 'command')) + setattr(cfg_args, args.backend, cfg_args.command) + else: + # [3] + if oneutils.is_valid_attr(cfg_args, 'backend'): + assert (oneutils.is_valid_attr(cfg_args, 'command')) + given_backends = [cfg_args.backend] + setattr(cfg_args, cfg_args.backend, cfg_args.command) + # [4] + if oneutils.is_valid_attr(cfg_args, 'backends'): + given_backends = cfg_args.backends.split(',') + # [5], [6] + else: + assert (backend_args or unknown_args) + given_backends = [args.backend] + + for given_backend in given_backends: + # make a command to run given backend driver + codegen_path = None + backend_base = given_backend + '-compile' + for cand in backends_list: + if ntpath.basename(cand) == backend_base: + codegen_path = cand + if not codegen_path: + # Find backend from system path + codegen_path = shutil.which(backend_base) + + if not codegen_path: + raise FileNotFoundError(backend_base + ' not found') + + codegen_cmd = [codegen_path] + if not cmd_overwrite and oneutils.is_valid_attr(cfg_args, given_backend): + codegen_cmd += getattr(cfg_args, given_backend).split() + else: + codegen_cmd += backend_args + codegen_cmd += unknown_args + + # run backend driver + oneutils.run(codegen_cmd, err_prefix=backend_base) if __name__ == '__main__': diff --git a/compiler/one-cmds/one-import-onnx b/compiler/one-cmds/one-import-onnx index 24edea6..b9c773b 100644 --- a/compiler/one-cmds/one-import-onnx +++ b/compiler/one-cmds/one-import-onnx @@ -41,6 +41,89 @@ import onelib.utils as oneutils sys.tracebacklimit = 0 +# Class to rename input/output to prevent issues while import ONNX models +class TidyIONames: + def __init__(self, onnx_model): + self.input_nodes = [] + self.output_nodes = [] + self.remap_inputs = [] + self.remap_outputs = [] + self.initializers = [] + self.onnx_model = onnx_model + # some models may have initializers as inputs. ignore them. + for initializer in onnx_model.graph.initializer: + self.initializers.append(initializer.name) + + def order(self): + for idx in range(0, len(self.onnx_model.graph.input)): + name = self.onnx_model.graph.input[idx].name + if not name in self.initializers: + self.input_nodes.append(name) + self.remap_inputs.append('i_' + format(idx + 1, '04d') + '_' + name) + for idx in range(0, len(self.onnx_model.graph.output)): + name = self.onnx_model.graph.output[idx].name + self.output_nodes.append(name) + self.remap_outputs.append('o_' + format(idx + 1, '04d') + '_' + name) + + # exclude special characters in names + def sanitize(self): + for idx in range(0, len(self.onnx_model.graph.input)): + name = self.onnx_model.graph.input[idx].name + if not name in self.initializers: + if '.' in name or ':' in name or name[:1].isdigit(): + self.input_nodes.append(name) + name_alt = name.replace('.', '_') + name_alt = name_alt.replace(':', '_') + if name_alt[:1].isdigit(): + name_alt = 'a_' + name_alt + self.remap_inputs.append(name_alt) + for idx in range(0, len(self.onnx_model.graph.output)): + name = self.onnx_model.graph.output[idx].name + if '.' in name or ':' in name or name[:1].isdigit(): + self.output_nodes.append(name) + name_alt = name.replace('.', '_') + name_alt = name_alt.replace(':', '_') + if name_alt[:1].isdigit(): + name_alt = 'a_' + name_alt + self.remap_outputs.append(name_alt) + + def update(self): + # change names for graph input + for i in range(len(self.onnx_model.graph.input)): + if self.onnx_model.graph.input[i].name in self.input_nodes: + to_rename = self.onnx_model.graph.input[i].name + idx = self.input_nodes.index(to_rename) + self.onnx_model.graph.input[i].name = self.remap_inputs[idx] + # change names of all nodes in the graph + for i in range(len(self.onnx_model.graph.node)): + # check node.input is to change to remap_inputs or remap_outputs + for j in range(len(self.onnx_model.graph.node[i].input)): + if self.onnx_model.graph.node[i].input[j] in self.input_nodes: + to_rename = self.onnx_model.graph.node[i].input[j] + idx = self.input_nodes.index(to_rename) + self.onnx_model.graph.node[i].input[j] = self.remap_inputs[idx] + if self.onnx_model.graph.node[i].input[j] in self.output_nodes: + to_rename = self.onnx_model.graph.node[i].input[j] + idx = self.output_nodes.index(to_rename) + self.onnx_model.graph.node[i].input[j] = self.remap_outputs[idx] + # check node.output is to change to remap_inputs or remap_outputs + for j in range(len(self.onnx_model.graph.node[i].output)): + if self.onnx_model.graph.node[i].output[j] in self.output_nodes: + to_rename = self.onnx_model.graph.node[i].output[j] + idx = self.output_nodes.index(to_rename) + self.onnx_model.graph.node[i].output[j] = self.remap_outputs[idx] + if self.onnx_model.graph.node[i].output[j] in self.input_nodes: + to_rename = self.onnx_model.graph.node[i].output[j] + idx = self.input_nodes.index(to_rename) + self.onnx_model.graph.node[i].output[j] = self.remap_inputs[idx] + # change names for graph output + for i in range(len(self.onnx_model.graph.output)): + if self.onnx_model.graph.output[i].name in self.output_nodes: + to_rename = self.onnx_model.graph.output[i].name + idx = self.output_nodes.index(to_rename) + self.onnx_model.graph.output[i].name = self.remap_outputs[idx] + + def get_driver_cfg_section(): return "one-import-onnx" @@ -135,63 +218,32 @@ def _apply_verbosity(verbosity): os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' +# TF2.12.1 tries to sanitize special characters, '.:' and maybe others and then fails with +# 'IndexError: tuple index out of range' error from somewhere else. +# This method is to prevent this IndexError. +def _sanitize_io_names(onnx_model): + sanitizer = TidyIONames(onnx_model) + sanitizer.sanitize() + sanitizer.update() + + # The index of input/output is added in front of the name. For example, # Original input names: 'a', 'c', 'b' -# Renamed: '0001_a', '0002_c', '0003_b' +# Renamed: 'i_0001_a', 'i_0002_c', 'i_0003_b' # This will preserve I/O order after import. def _remap_io_names(onnx_model): # gather existing name of I/O and generate new name of I/O in sort order - input_nodes = [] - output_nodes = [] - remap_inputs = [] - remap_outputs = [] - initializers = [] - # some models may have initializers as inputs. ignore them. - for initializer in onnx_model.graph.initializer: - initializers.append(initializer.name) - for idx in range(0, len(onnx_model.graph.input)): - name = onnx_model.graph.input[idx].name - if not name in initializers: - input_nodes.append(name) - remap_inputs.append(format(idx + 1, '04d') + '_' + name) - for idx in range(0, len(onnx_model.graph.output)): - name = onnx_model.graph.output[idx].name - output_nodes.append(name) - remap_outputs.append(format(idx + 1, '04d') + '_' + name) - # change names for graph input - for i in range(len(onnx_model.graph.input)): - if onnx_model.graph.input[i].name in input_nodes: - to_rename = onnx_model.graph.input[i].name - idx = input_nodes.index(to_rename) - onnx_model.graph.input[i].name = remap_inputs[idx] - # change names of all nodes in the graph - for i in range(len(onnx_model.graph.node)): - # check node.input is to change to remap_inputs or remap_outputs - for j in range(len(onnx_model.graph.node[i].input)): - if onnx_model.graph.node[i].input[j] in input_nodes: - to_rename = onnx_model.graph.node[i].input[j] - idx = input_nodes.index(to_rename) - onnx_model.graph.node[i].input[j] = remap_inputs[idx] - if onnx_model.graph.node[i].input[j] in output_nodes: - to_rename = onnx_model.graph.node[i].input[j] - idx = output_nodes.index(to_rename) - onnx_model.graph.node[i].input[j] = remap_outputs[idx] - # check node.output is to change to remap_inputs or remap_outputs - for j in range(len(onnx_model.graph.node[i].output)): - if onnx_model.graph.node[i].output[j] in output_nodes: - to_rename = onnx_model.graph.node[i].output[j] - idx = output_nodes.index(to_rename) - onnx_model.graph.node[i].output[j] = remap_outputs[idx] - if onnx_model.graph.node[i].output[j] in input_nodes: - to_rename = onnx_model.graph.node[i].output[j] - idx = input_nodes.index(to_rename) - onnx_model.graph.node[i].output[j] = remap_inputs[idx] - # change names for graph output - for i in range(len(onnx_model.graph.output)): - if onnx_model.graph.output[i].name in output_nodes: - to_rename = onnx_model.graph.output[i].name - idx = output_nodes.index(to_rename) - onnx_model.graph.output[i].name = remap_outputs[idx] + remapper = TidyIONames(onnx_model) + remapper.order() + remapper.update() + + +def _check_ext(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + ext_path = os.path.join(dir_path, 'one-import-onnx-ext') + if (os.path.isfile(ext_path)): + return ext_path + return None def _convert(args): @@ -200,6 +252,7 @@ def _convert(args): # get file path to log dir_path = os.path.dirname(os.path.realpath(__file__)) logfile_path = os.path.realpath(args.output_path) + '.log' + ext_path = _check_ext() with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir: # save intermediate @@ -207,6 +260,7 @@ def _convert(args): tmpdir = os.path.dirname(logfile_path) # convert onnx to tf saved model onnx_model = onnx.load(getattr(args, 'input_path')) + _sanitize_io_names(onnx_model) if _onnx_legalizer_enabled: options = onnx_legalizer.LegalizeOptions options.unroll_rnn = oneutils.is_valid_attr(args, 'unroll_rnn') @@ -219,6 +273,30 @@ def _convert(args): fixed_path = os.path.join(tmpdir, os.path.splitext(basename)[0] + '~.onnx') onnx.save(onnx_model, fixed_path) + + if ext_path: + # save onnx_model to temporary alt file + basename = os.path.basename(getattr(args, 'input_path')) + alt_path = os.path.join(tmpdir, os.path.splitext(basename)[0] + '-alt.onnx') + onnx.save(onnx_model, alt_path) + + # call extension with options + ext_cmd = [ext_path] + if oneutils.is_valid_attr(args, 'unroll_rnn'): + ext_cmd.append('--unroll_rnn') + if oneutils.is_valid_attr(args, 'unroll_lstm'): + ext_cmd.append('--unroll_lstm') + if oneutils.is_valid_attr(args, 'experimental_disable_batchmatmul_unfold'): + ext_cmd.append('--experimental_disable_batchmatmul_unfold') + if oneutils.is_valid_attr(args, 'save_intermediate'): + ext_cmd.append('--save_intermediate') + if oneutils.is_valid_attr(args, 'keep_io_order'): + ext_cmd.append('--keep_io_order') + ext_cmd.append(alt_path) + ext_cmd.append(getattr(args, 'output_path')) + oneutils.run(ext_cmd, logfile=f) + return + tf_savedmodel = onnx_tf.backend.prepare(onnx_model) savedmodel_name = os.path.splitext(os.path.basename( diff --git a/compiler/one-cmds/one-import-pytorch b/compiler/one-cmds/one-import-pytorch index 3a61d22..f0dbf41 100644 --- a/compiler/one-cmds/one-import-pytorch +++ b/compiler/one-cmds/one-import-pytorch @@ -20,7 +20,8 @@ # limitations under the License. import argparse -import importlib +import importlib.machinery +import importlib.util import inspect import os import sys diff --git a/compiler/one-cmds/one-infer b/compiler/one-cmds/one-infer index 125db3e..075e2bf 100644 --- a/compiler/one-cmds/one-infer +++ b/compiler/one-cmds/one-infer @@ -27,48 +27,13 @@ import ntpath import os import sys +import onelib.backends as backends import onelib.utils as oneutils # TODO Find better way to suppress trackback on error sys.tracebacklimit = 0 -def _search_backend_driver(driver): - """ - [one hierarchy] - one - ├── backends - ├── bin - ├── doc - ├── include - ├── lib - ├── optimization - └── test - - The list where `one-infer` finds its backend driver - - `bin` folder where `one-infer` exists - - `backends/**/bin/` folder - - NOTE If there are drivers of the same name in different places, - the closer to the top in the list, the higher the priority. - """ - dir_path = os.path.dirname(os.path.realpath(__file__)) - - # CASE 1: one/bin/{driver} is found - driver_path = dir_path + '/' + driver - if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): - return driver_path - - # CASE 2: one/backends/**/bin/{driver} is found - for driver_path in glob.glob( - dir_path + '/../backends/**/bin/' + driver, recursive=True): - if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): - return driver_path - - # CASE 3: {driver} is found in nowhere - return None - - def _get_parser(): infer_usage = 'one-infer [-h] [-v] [-C CONFIG] [-d DRIVER] [--post-process POST_PROCESS] [--] [COMMANDS FOR BACKEND DRIVER]' infer_detail = """ @@ -128,7 +93,7 @@ def _parse_arg(parser): def _get_executable(args): driver = oneutils.is_valid_attr(args, 'driver') - executable = _search_backend_driver(driver) + executable = backends.search_driver(driver) if executable: return executable else: diff --git a/compiler/one-cmds/one-init b/compiler/one-cmds/one-init index 299255c..f2ddc78 100644 --- a/compiler/one-cmds/one-init +++ b/compiler/one-cmds/one-init @@ -28,6 +28,7 @@ import os import sys import configparser +import onelib.backends as backends import onelib.utils as oneutils # TODO Find better way to suppress trackback on error @@ -85,45 +86,6 @@ class CommentableConfigParser(configparser.ConfigParser): self[section][f'{comment_sign} {comment}'] = None -def _get_backends_list(): - """ - [one hierarchy] - one - ├── backends - ├── bin - ├── doc - ├── include - ├── lib - ├── optimization - └── test - - The list where `one-init` finds its backends - - `bin` folder where `one-init` exists - - `backends` folder - - NOTE If there are backends of the same name in different places, - the closer to the top in the list, the higher the priority. - """ - dir_path = os.path.dirname(os.path.realpath(__file__)) - backend_set = set() - - # bin folder - files = [f for f in glob.glob(dir_path + '/*-init')] - # backends folder - files += [f for f in glob.glob(dir_path + '/../backends/**/*-init', recursive=True)] - # TODO find backends in `$PATH` - - backends_list = [] - for cand in files: - base = ntpath.basename(cand) - if (not base in backend_set) and os.path.isfile(cand) and os.access( - cand, os.X_OK): - backend_set.add(base) - backends_list.append(cand) - - return backends_list - - # TODO Add support for TF graphdef and bcq def _get_parser(backends_list): init_usage = ( @@ -348,7 +310,7 @@ def _get_model_type(parser, args): def main(): # get backend list - backends_list = _get_backends_list() + backends_list = backends.get_list('init') # parse arguments parser = _get_parser(backends_list) diff --git a/compiler/one-cmds/one-prepare-venv b/compiler/one-cmds/one-prepare-venv index baa88be..a456a6b 100644 --- a/compiler/one-cmds/one-prepare-venv +++ b/compiler/one-cmds/one-prepare-venv @@ -26,16 +26,16 @@ VENV_PYTHON=${DRIVER_PATH}/venv/bin/python if [ ! -f ${VENV_ACTIVATE} ]; then # Create python virtual enviornment - python3.8 -m venv "${DRIVER_PATH}/venv" + python3 -m venv "${DRIVER_PATH}/venv" fi # NOTE version # - https://github.com/onnx/onnx/blob/master/docs/Versioning.md # - https://github.com/onnx/onnx-tensorflow/blob/master/Versioning.md -VER_TENSORFLOW=2.8.0 -VER_ONNX=1.11.0 -VER_ONNXRUNTIME=1.11.0 +VER_TENSORFLOW=2.12.1 +VER_ONNX=1.14.0 +VER_ONNXRUNTIME=1.15.0 VER_ONNX_TF=1.10.0 VER_PYDOT=1.4.2 @@ -64,10 +64,9 @@ else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow-cpu==${VER_TENSORFLOW} fi ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install Pillow -# TODO remove version fix, https://github.com/Samsung/ONE/issues/9240 -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability==0.16.0 -# TODO remove version fix, https://github.com/Samsung/ONE/issues/10481 -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_addons==0.16.1 +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability==0.20.1 +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_addons==0.20.0 # Install PyTorch and ONNX related # NOTE set ONE_PREPVENV_TORCH_STABLE to override 'torch_stable.html' URL. @@ -79,7 +78,7 @@ if [[ ! -z "$ONE_PREPVENV_TORCH_STABLE" ]]; then fi # TODO remove torch message echo "Torch from '${ONE_PREPVENV_TORCH_STABLE}' -> '${TORCH_STABLE_URL}'" -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install torch==1.11.0+cpu -f ${TORCH_STABLE_URL} +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install torch==1.13.1+cpu -f ${TORCH_STABLE_URL} ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx==${VER_ONNX} @@ -92,9 +91,8 @@ else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx-tf==${VER_ONNX_TF} fi -# NOTE refer https://github.com/protocolbuffers/protobuf/issues/10051 -# TODO remove this when issue is resolved -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==3.20.1 +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==4.23.3 # Install pydot for visq ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install pydot==${VER_PYDOT} diff --git a/compiler/one-cmds/one-prepare-venv.aarch64 b/compiler/one-cmds/one-prepare-venv.aarch64 new file mode 100644 index 0000000..c8850df --- /dev/null +++ b/compiler/one-cmds/one-prepare-venv.aarch64 @@ -0,0 +1,139 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +DRIVER_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +VENV_ACTIVATE=${DRIVER_PATH}/venv/bin/activate +# NOTE please use venv's python instead of python after `source activation`. +# This script is called by debian maintainer script, i.e. `postinst`. +# Since debian maintainer script is called with sudo, `source activation` is ignored. +VENV_PYTHON=${DRIVER_PATH}/venv/bin/python + +if [ ! -f ${VENV_ACTIVATE} ]; then + # Create python virtual enviornment + python3 -m venv "${DRIVER_PATH}/venv" +fi + +# NOTE version +# - https://github.com/onnx/onnx/blob/master/docs/Versioning.md +# - https://github.com/onnx/onnx-tensorflow/blob/master/Versioning.md + +VER_TENSORFLOW=2.12.1 +VER_ONNX=1.14.0 +VER_ONNXRUNTIME=1.15.0 +VER_ONNX_TF=1.10.0 +VER_PYDOT=1.4.2 + +# Install tensorflow + +PIP_TRUSTED_HOST="--trusted-host pypi.org " +PIP_TRUSTED_HOST+="--trusted-host pypi.python.org " +PIP_TRUSTED_HOST+="--trusted-host files.pythonhosted.org " +PIP_TRUSTED_HOST+="--trusted-host download.pytorch.org " + +PIP_TIMEOUT="--default-timeout=1000 " + +PIP_OPTIONS="${PIP_TIMEOUT} ${PIP_TRUSTED_HOST}" + +# NOTE $ONE_PREPVENV_PIP_OPTION is to provide additional PIP options +# such as ceritificate file behind firewall +# ex) ONE_PREPVENV_PIP_OPTION="--cert SomePrivateCetificate.crt" ./one-prepare-venv +if [[ ! -z "$ONE_PREPVENV_PIP_OPTION" ]]; then + PIP_OPTIONS+=" ${ONE_PREPVENV_PIP_OPTION} " +fi + +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade pip setuptools +if [ -n "${EXT_TENSORFLOW_WHL}" ]; then + ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install ${EXT_TENSORFLOW_WHL} +else + ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow==${VER_TENSORFLOW} +fi +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install Pillow +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability==0.20.1 +#${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_addons==0.20.0 + +# NOTE +# +# - Since tensorflow_addons 0.20.0 package distribution does not exist, it is +# configured to build using the source code at the time of +# one-prepare-env. This is not a perfect solution as it requires a build +# environment at install time. +# +# - Later, it is necessary to change the pre-built package to be uploaded +# and downloaded at the time of installation. (Or expect the appropriate +# tensorflow_addons official package to be distributed.) + +# Make tempolary workspace for build +BAZEL_BUILD_PATH=$(mktemp -d) +pushd $BAZEL_BUILD_PATH +source $VENV_ACTIVATE + +# Download tensorflow_addons source +git clone https://github.com/tensorflow/addons.git +cd addons +git checkout -b r0.20 origin/r0.20 + +# Install bazel +wget https://github.com/bazelbuild/bazelisk/releases/download/v1.17.0/bazelisk-linux-arm64 +chmod 755 bazelisk-linux-arm64 +ln -s bazelisk-linux-arm64 bazel + +# This script links project with TensorFlow dependency +python3 ./configure.py + +# Build +./bazel build build_pip_pkg +bazel-bin/build_pip_pkg artifacts + +# Install tensroflow_addons +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install artifacts/tensorflow_addons-*.whl + +# Remove tempolary workspace +deactivate +popd +rm -rf $BAZEL_BUILD_PATH + +# Install PyTorch and ONNX related +# NOTE set ONE_PREPVENV_TORCH_STABLE to override 'torch_stable.html' URL. +# torch_stable.html points to download URL of torch wheel file(s) +# but sometimes the server gets unstable, especially from in-house CI. +TORCH_STABLE_URL="https://download.pytorch.org/whl/torch_stable.html" +if [[ ! -z "$ONE_PREPVENV_TORCH_STABLE" ]]; then + TORCH_STABLE_URL="${ONE_PREPVENV_TORCH_STABLE}" +fi +# TODO remove torch message +echo "Torch from '${ONE_PREPVENV_TORCH_STABLE}' -> '${TORCH_STABLE_URL}'" +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install torch==1.13.1 -f ${TORCH_STABLE_URL} + +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx==${VER_ONNX} + +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnxruntime==${VER_ONNXRUNTIME} + +# Provide install of custom onnx-tf +if [ -n "${EXT_ONNX_TF_WHL}" ]; then + ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install ${EXT_ONNX_TF_WHL} +else + ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx-tf==${VER_ONNX_TF} +fi + +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==4.23.3 + +# Install pydot for visq +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install pydot==${VER_PYDOT} diff --git a/compiler/one-cmds/one-prepare-venv.u2204 b/compiler/one-cmds/one-prepare-venv.u1804 similarity index 90% rename from compiler/one-cmds/one-prepare-venv.u2204 rename to compiler/one-cmds/one-prepare-venv.u1804 index d4b0467..55c79c6 100644 --- a/compiler/one-cmds/one-prepare-venv.u2204 +++ b/compiler/one-cmds/one-prepare-venv.u1804 @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,16 +26,16 @@ VENV_PYTHON=${DRIVER_PATH}/venv/bin/python if [ ! -f ${VENV_ACTIVATE} ]; then # Create python virtual enviornment - python3 -m venv "${DRIVER_PATH}/venv" + python3.8 -m venv "${DRIVER_PATH}/venv" fi # NOTE version # - https://github.com/onnx/onnx/blob/master/docs/Versioning.md # - https://github.com/onnx/onnx-tensorflow/blob/master/Versioning.md -VER_TENSORFLOW=2.10.1 -VER_ONNX=1.12.0 -VER_ONNXRUNTIME=1.12.1 +VER_TENSORFLOW=2.12.1 +VER_ONNX=1.14.0 +VER_ONNXRUNTIME=1.15.0 VER_ONNX_TF=1.10.0 VER_PYDOT=1.4.2 @@ -64,7 +64,9 @@ else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow-cpu==${VER_TENSORFLOW} fi ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install Pillow -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_probability==0.20.1 +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install tensorflow_addons==0.20.0 # Install PyTorch and ONNX related # NOTE set ONE_PREPVENV_TORCH_STABLE to override 'torch_stable.html' URL. @@ -89,9 +91,8 @@ else ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install onnx-tf==${VER_ONNX_TF} fi -# NOTE refer https://github.com/protocolbuffers/protobuf/issues/10051 -# TODO remove this when issue is resolved -${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==3.19.6 +# Fix version to that of TF release date +${VENV_PYTHON} -m pip ${PIP_OPTIONS} install --upgrade protobuf==4.23.3 # Install pydot for visq ${VENV_PYTHON} -m pip ${PIP_OPTIONS} install pydot==${VER_PYDOT} diff --git a/compiler/one-cmds/one-profile b/compiler/one-cmds/one-profile index 18a0456..bc5338d 100644 --- a/compiler/one-cmds/one-profile +++ b/compiler/one-cmds/one-profile @@ -26,6 +26,7 @@ import itertools import ntpath import os import sys +from types import SimpleNamespace import onelib.utils as oneutils @@ -92,15 +93,46 @@ def _get_parser(backends_list): return parser -def _verify_arg(parser, args): +def _verify_arg(parser, args, cfg_args, backend_args, unknown_args): """verify given arguments""" + cmd_backend_exist = oneutils.is_valid_attr(args, 'backend') + cfg_backend_exist = oneutils.is_valid_attr(cfg_args, 'backend') + cfg_backends_exist = oneutils.is_valid_attr(cfg_args, 'backends') + # check if required arguments is given missing = [] - if not oneutils.is_valid_attr(args, 'backend'): + if not cmd_backend_exist and not cfg_backend_exist and not cfg_backends_exist: missing.append('-b/--backend') if len(missing): parser.error('the following arguments are required: ' + ' '.join(missing)) + if not oneutils.is_valid_attr(args, 'config'): + if not backend_args and not unknown_args: + parser.error('commands for the backend is missing.') + + if cfg_backend_exist and cfg_backends_exist: + parser.error( + '\'backend\' option and \'backends\' option cannot be used simultaneously.') + + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist and cfg_backend_exist: + if args.backend != cfg_args.backend: + parser.error('Not found the command of given backend') + + if cfg_backend_exist and not oneutils.is_valid_attr(cfg_args, 'command'): + parser.error('\'command\' key is missing in the configuration file.') + + if cfg_backends_exist: + cfg_backends = getattr(cfg_args, 'backends').split(',') + # check if commands of given backends exist + for b in cfg_backends: + if not oneutils.is_valid_attr(cfg_args, b): + parser.error('Not found the command for ' + b) + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist: + if args.backend not in cfg_backends: + parser.error('Not found the command of given backend') + def _parse_arg(parser): profile_args = [] @@ -139,25 +171,75 @@ def main(): args, backend_args, unknown_args = _parse_arg(parser) # parse configuration file - oneutils.parse_cfg(args.config, 'one-profile', args) + cfg_args = SimpleNamespace() + oneutils.parse_cfg(args.config, 'one-profile', cfg_args) # verify arguments - _verify_arg(parser, args) - - # make a command to run given backend driver - profile_path = None - backend_base = getattr(args, 'backend') + '-profile' - for cand in backends_list: - if ntpath.basename(cand) == backend_base: - profile_path = cand - if not profile_path: - raise FileNotFoundError(backend_base + ' not found') - profile_cmd = [profile_path] + backend_args + unknown_args - if oneutils.is_valid_attr(args, 'command'): - profile_cmd += getattr(args, 'command').split() - - # run backend driver - oneutils.run(profile_cmd, err_prefix=backend_base) + _verify_arg(parser, args, cfg_args, backend_args, unknown_args) + ''' + one-profile defines its behavior for below cases. + + [1] one-profile -h + [2] one-profile -v + [3] one-profile -C ${cfg} (backend, command key in cfg) + [4] one-profile -C ${cfg} (backends key in cfg) + [5] one-profile -b ${backend} ${command} + [6] one-profile -b ${backend} -- ${command} + [7] one-profile -b ${backend} -C {cfg} (backend, command key in cfg) + [8] one-profile -b ${backend} -C {cfg} (backends key in cfg) (Only 'backend' is invoked, + even though cfg file has multiple backends) + [9] one-profile -b ${backend} -C ${cfg} -- ${command} (backend, command key in cfg) + [10] one-profile -b ${backend} -C ${cfg} -- ${command} (backends key in cfg) (Only 'backend' is invoked, + even though cfg file has multiple backends) + + All other cases are not allowed or an undefined behavior. + ''' + cmd_overwrite = False + if oneutils.is_valid_attr(args, 'config'): + # [9], [10] + if backend_args and not unknown_args: + given_backends = [args.backend] + cmd_overwrite = True + else: + # [7], [8] + if oneutils.is_valid_attr(args, 'backend'): + given_backends = [args.backend] + if oneutils.is_valid_attr(cfg_args, 'backend'): + assert (oneutils.is_valid_attr(cfg_args, 'command')) + setattr(cfg_args, args.backend, cfg_args.command) + else: + # [3] + if oneutils.is_valid_attr(cfg_args, 'backend'): + assert (oneutils.is_valid_attr(cfg_args, 'command')) + given_backends = [cfg_args.backend] + setattr(cfg_args, cfg_args.backend, cfg_args.command) + # [4] + if oneutils.is_valid_attr(cfg_args, 'backends'): + given_backends = cfg_args.backends.split(',') + # [5], [6] + else: + assert (backend_args or unknown_args) + given_backends = [args.backend] + + for given_backend in given_backends: + # make a command to run given backend driver + profile_path = None + backend_base = given_backend + '-profile' + for cand in backends_list: + if ntpath.basename(cand) == backend_base: + profile_path = cand + if not profile_path: + raise FileNotFoundError(backend_base + ' not found') + + profile_cmd = [profile_path] + if not cmd_overwrite and oneutils.is_valid_attr(cfg_args, given_backend): + profile_cmd += getattr(cfg_args, given_backend).split() + else: + profile_cmd += backend_args + profile_cmd += unknown_args + + # run backend driver + oneutils.run(profile_cmd, err_prefix=backend_base) if __name__ == '__main__': diff --git a/compiler/one-cmds/one-quantize b/compiler/one-cmds/one-quantize index 060892d..f686aad 100644 --- a/compiler/one-cmds/one-quantize +++ b/compiler/one-cmds/one-quantize @@ -121,6 +121,18 @@ def _get_parser(): 'maximum percentile (0.0~100.0, default=99.0). Algorithm parameter for calibration. This is valid when calibration algorithm is percentile.' ) quantization_group.add_argument( + '--moving_avg_batch', + type=str, + help= + 'batch size of moving average (default=16). This is valid when calibration algorithm is moving_average.' + ) + quantization_group.add_argument( + '--moving_avg_const', + type=str, + help= + 'hyperparameter (C) to compute moving average (default=0.1). Update equation: avg <- avg + C * (curr_batch_avg - avg). This is valid when calibration algorithm is moving_average.' + ) + quantization_group.add_argument( '--mode', type=str, help= @@ -217,6 +229,38 @@ def _get_parser(): action='store_true', help='convert quantized model to fake-quantized fp32 model.') + # arguments for requantize option + requantize_group = parser.add_argument_group('arguments for requantize option') + + requantize_group.add_argument( + '--requantize', + action='store_true', + help='convert quantized model to another-typed quantized model (ex: int8 -> uin8).' + ) + + # arguments for ampq option + ampq_quant_group = parser.add_argument_group('arguments for ampq option') + # ampq + ampq_quant_group.add_argument( + '--ampq', action='store_true', help='quantize model using ampq solver.') + + # ampq_qerror_ratio + ampq_quant_group.add_argument( + '--ampq_qerror_ratio', type=str, help='quantization error ratio ([0, 1])') + + # ampq_algorithm + ampq_quant_group.add_argument( + '--ampq_algorithm', type=str, help='type of algorithm (bisection)') + + ampq_quant_group.add_argument( + '--bisection_type', type=str, help="one of 'auto', 'i16_front', 'i16_back'") + + # ampq_bisection_visq + ampq_quant_group.add_argument( + '--ampq_bisection_visq', + type=str, + help='.visq.json file path with quantization errors') + return parser @@ -256,6 +300,29 @@ def _set_default_values(args): setattr(args, 'min_percentile', '1.0') if not oneutils.is_valid_attr(args, 'max_percentile'): setattr(args, 'max_percentile', '99.0') + if not oneutils.is_valid_attr(args, 'moving_avg_batch'): + setattr(args, 'moving_avg_batch', '16') + if not oneutils.is_valid_attr(args, 'moving_avg_const'): + setattr(args, 'moving_avg_const', '0.1') + if not oneutils.is_valid_attr(args, 'ampq_algorithm'): + setattr(args, 'ampq_algorithm', 'bisection') + if not oneutils.is_valid_attr(args, 'bisection_type'): + setattr(args, 'bisection_type', 'auto') + + +def _verify_arg_pre(parser, args): + """verify given arguments before default values are set""" + # check if required arguments is given + missing = [] + if oneutils.is_valid_attr(args, 'requantize'): + if not oneutils.is_valid_attr(args, + 'input_model_dtype') and not oneutils.is_valid_attr( + args, 'input_dtype'): + missing.append('--input_model_dtype') + if not oneutils.is_valid_attr(args, 'quantized_dtype'): + missing.append('--quantized_dtype') + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) def _verify_arg(parser, args): @@ -294,6 +361,31 @@ def _verify_arg(parser, args): parser.error( 'The same number of src_tensor_name and dst_tensor_name should be given.') + # Check calibration parameters + if oneutils.is_valid_attr(args, 'mode'): + if getattr(args, 'mode') == 'percentile': + # Check dtype + try: + min_percentile = float(getattr(args, 'min_percentile')) + except ValueError: + parser.error('min_percentile must be float') + try: + max_percentile = float(getattr(args, 'max_percentile')) + except ValueError: + parser.error('max_percentile must be float') + elif getattr(args, 'mode') == 'moving_average': + # Check dtype + try: + moving_avg_batch = int(getattr(args, 'moving_avg_batch')) + except ValueError: + parser.error('moving_avg_batch must be integer') + try: + moving_avg_const = float(getattr(args, 'moving_avg_const')) + except ValueError: + parser.error('moving_avg_const must be float') + else: + parser.error('Unsupported mode') + def _parse_arg(parser): args = parser.parse_args() @@ -305,6 +397,10 @@ def _parse_arg(parser): def _quantize(args): + if oneutils.is_valid_attr(args, 'ampq'): + _ampq_solve(args) + return + if oneutils.is_valid_attr(args, 'force_quantparam'): # write quantization parameters _write_qparam(args) @@ -320,6 +416,11 @@ def _quantize(args): _fake_quantize(args) return + if oneutils.is_valid_attr(args, 'requantize'): + # requantize model + _requantize(args) + return + # get file path to log dir_path = os.path.dirname(os.path.realpath(__file__)) logfile_path = os.path.realpath(args.output_path) + '.log' @@ -383,6 +484,8 @@ def _quantize(args): .add_option_with_valid_args('--input_data_format', ['input_data_format']) \ .add_option_with_valid_args('--min_percentile', ['min_percentile']) \ .add_option_with_valid_args('--max_percentile', ['max_percentile']) \ + .add_option_with_valid_args('--moving_avg_batch', ['moving_avg_batch']) \ + .add_option_with_valid_args('--moving_avg_const', ['moving_avg_const']) \ .add_option_with_valid_args('--mode', ['mode']) \ .add_noarg_option_if_valid_arg('--generate_profile_data', 'generate_profile_data') \ .run() @@ -551,6 +654,204 @@ def _fake_quantize(args): .run() +def _ampq_solve(args): + # get file path to log + dir_path = os.path.dirname(os.path.realpath(__file__)) + logfile_path = os.path.realpath(args.output_path) + '.log' + + with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir: + if oneutils.is_valid_attr(args, 'save_intermediate'): + tmpdir = os.path.dirname(logfile_path) + + # get driver path + record_minmax_path = os.path.join(dir_path, 'record-minmax') + + tmp_minmax_recorded_path = os.path.join( + tmpdir, + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.minmax_recorded.circle' + + ## make a command to record min-max value of each tensor while running the representative dataset + record_minmax_cmd = Command(record_minmax_path, args, f) + record_minmax_cmd.add_noarg_option_if_valid_arg('--verbose', 'verbose') \ + .add_option_with_valid_args('--input_model', ['input_path']) \ + .add_option_with_values('--output_model', [tmp_minmax_recorded_path]) \ + .add_option_with_valid_args('--input_data', ['input_data']) \ + .add_option_with_valid_args('--input_data_format', ['input_data_format']) \ + .add_option_with_valid_args('--min_percentile', ['min_percentile']) \ + .add_option_with_valid_args('--max_percentile', ['max_percentile']) \ + .add_option_with_valid_args('--moving_avg_batch', ['moving_avg_batch']) \ + .add_option_with_valid_args('--moving_avg_const', ['moving_avg_const']) \ + .add_option_with_valid_args('--mode', ['mode']) \ + .add_noarg_option_if_valid_arg('--generate_profile_data', 'generate_profile_data') \ + .run() + + # process visq if needed + visq_file = None + if oneutils.is_valid_attr(args, 'ampq_bisection_visq'): + visq_file = getattr(args, 'ampq_bisection_visq') + + if (oneutils.is_valid_attr(args, 'ampq_algorithm') + and oneutils.is_valid_attr(args, 'bisection_type')): + algorithm = getattr(args, 'ampq_algorithm') + bisection_type = getattr(args, 'bisection_type') + if algorithm == 'bisection' and bisection_type == 'auto' and visq_file is None: + # algorithm needs bisection but no file in input configuration + + # to compute visq file we need q8 quantized model + q8_file = os.path.join( + tmpdir, + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.visq.q8.circle' + + # get drievr path + circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer') + circle_quantizer_cmd = [circle_quantizer_path] + # verbose + if oneutils.is_valid_attr(args, 'verbose'): + circle_quantizer_cmd.append('--verbose') + circle_quantizer_cmd.append('--quantize_with_minmax') + circle_quantizer_cmd.append('float32') + circle_quantizer_cmd.append('uint8') + circle_quantizer_cmd.append('channel') + + if oneutils.is_valid_attr(args, 'TF-style_maxpool'): + circle_quantizer_cmd.append('--TF-style_maxpool') + + circle_quantizer_cmd.extend(['--input_type', 'uint8']) + circle_quantizer_cmd.extend(['--output_type', 'uint8']) + + # input and output paths + circle_quantizer_cmd.append(tmp_minmax_recorded_path) + circle_quantizer_cmd.append(q8_file) + + f.write((' '.join(circle_quantizer_cmd) + '\n').encode()) + + # run circle-quantizer + oneutils.run( + circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f) + + # compute visq file + visq_path = os.path.join(dir_path, 'visq') + + visq_file = os.path.join( + tmpdir, + os.path.splitext(os.path.basename( + args.input_path))[0]) + '.tae.visq.json' + + visq_cmd = [visq_path] + visq_cmd.extend(['--fp32_circle', getattr(args, 'input_path')]) + visq_cmd.extend(['--data', getattr(args, 'input_data')]) + visq_cmd.extend(['--q_circle', q8_file]) + visq_cmd.extend(['--tae_output', visq_file]) + visq_cmd.extend(['--batch_size', "1"]) + visq_cmd.append('--dump_dot_graph') + f.write((' '.join(visq_cmd) + '\n').encode()) + + # run visq + oneutils.run(visq_cmd, err_prefix="visq", logfile=f) + + # get driver path + circle_mpqsolver_path = os.path.join(dir_path, 'circle-mpqsolver') + + # solve for Mixed Precision Quantization configuration + ampq_quantize_cmd = [circle_mpqsolver_path] + + # data + if oneutils.is_valid_attr(args, 'input_data'): + ampq_quantize_cmd.extend(['--data', getattr(args, 'input_data')]) + + # data format + if oneutils.is_valid_attr(args, 'input_data_format'): + ampq_quantize_cmd.extend( + ['--data_format', getattr(args, 'input_data_format')]) + + # qerror_ratio + if oneutils.is_valid_attr(args, 'ampq_qerror_ratio'): + ampq_quantize_cmd.extend( + ['--qerror_ratio', getattr(args, 'ampq_qerror_ratio')]) + + # algorithm + if oneutils.is_valid_attr(args, 'ampq_algorithm'): + algorithm = getattr(args, 'ampq_algorithm') + if algorithm == 'bisection': + if oneutils.is_valid_attr(args, 'bisection_type'): + bisection_type = getattr(args, 'bisection_type') + if bisection_type == 'auto': + ampq_quantize_cmd.extend(['--bisection', 'auto']) + elif bisection_type == 'i16_front': + ampq_quantize_cmd.extend(['--bisection', 'true']) + elif bisection_type == 'i16_back': + ampq_quantize_cmd.extend(['--bisection', 'false']) + + # recorded model as input + ampq_quantize_cmd.extend(['--input_model', tmp_minmax_recorded_path]) + + # input_dtype + if oneutils.is_valid_attr(args, 'input_type'): + ampq_quantize_cmd.extend(['--input_dtype', getattr(args, 'input_type')]) + + # output dtype + if oneutils.is_valid_attr(args, 'output_type'): + ampq_quantize_cmd.extend(['--output_dtype', getattr(args, 'output_type')]) + + # output model + if oneutils.is_valid_attr(args, 'output_path'): + ampq_quantize_cmd.extend(['--output_model', getattr(args, 'output_path')]) + + # visq_file + if not (visq_file is None): + ampq_quantize_cmd.extend(['--visq_file', visq_file]) + + # save_intermediate + if oneutils.is_valid_attr(args, 'save_intermediate'): + intermediate_dir = os.path.dirname(logfile_path) + ampq_quantize_cmd.extend(['--save_intermediate', intermediate_dir]) + + if oneutils.is_valid_attr(args, 'verbose'): + ampq_quantize_cmd.append('--verbose') + + f.write((' '.join(ampq_quantize_cmd) + '\n').encode()) + + # run ampq + oneutils.run(ampq_quantize_cmd, err_prefix="circle_mpqsolver", logfile=f) + + +def _requantize(args): + # get file path to log + dir_path = os.path.dirname(os.path.realpath(__file__)) + logfile_path = os.path.realpath(args.output_path) + '.log' + + with open(logfile_path, 'wb') as f: + # get driver path + circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer') + + ## make a command to quantize and dequantize the weights of the model + circle_quantizer_cmd = [circle_quantizer_path] + # verbose + if oneutils.is_valid_attr(args, 'verbose'): + circle_quantizer_cmd.append('--verbose') + # requantize + circle_quantizer_cmd.append('--requantize') + # Use input_model_dtype if it exists. Use input_dtype otherwise. + if oneutils.is_valid_attr(args, 'input_model_dtype'): + circle_quantizer_cmd.append(getattr(args, 'input_model_dtype')) + elif oneutils.is_valid_attr(args, 'input_dtype'): + circle_quantizer_cmd.append(getattr(args, 'input_dtype')) + if oneutils.is_valid_attr(args, 'quantized_dtype'): + circle_quantizer_cmd.append(getattr(args, 'quantized_dtype')) + # input and output path + if oneutils.is_valid_attr(args, 'input_path'): + circle_quantizer_cmd.append(getattr(args, 'input_path')) + if oneutils.is_valid_attr(args, 'output_path'): + circle_quantizer_cmd.append(getattr(args, 'output_path')) + + f.write((' '.join(circle_quantizer_cmd) + '\n').encode()) + + # run circle-quantizer + oneutils.run(circle_quantizer_cmd, err_prefix="circle_quantizer", logfile=f) + + def main(): # parse arguments parser = _get_parser() @@ -559,6 +860,9 @@ def main(): # parse configuration file oneutils.parse_cfg(args.config, 'one-quantize', args) + # verify arguments before default value setting + _verify_arg_pre(parser, args) + # set default values _set_default_values(args) diff --git a/compiler/one-cmds/onecc b/compiler/one-cmds/onecc index 4cc7740..c7a76c5 100644 --- a/compiler/one-cmds/onecc +++ b/compiler/one-cmds/onecc @@ -24,6 +24,7 @@ import configparser import os import subprocess import sys +from types import SimpleNamespace from onelib.CfgRunner import CfgRunner from onelib.WorkflowRunner import WorkflowRunner @@ -67,7 +68,7 @@ def _check_subtool_exists(): def _get_parser(): - onecc_usage = 'onecc [-h] [-v] [-C CONFIG] [-W WORKFLOW] [-O OPTIMIZATION] [COMMAND ]' + onecc_usage = 'onecc [-h] [-v] [-C CONFIG] [-b BACKEND] [-W WORKFLOW] [-O OPTIMIZATION] [COMMAND ]' onecc_desc = 'Run ONE driver via several commands or configuration file' parser = argparse.ArgumentParser(description=onecc_desc, usage=onecc_usage) @@ -86,6 +87,9 @@ def _get_parser(): parser.add_argument( '-W', '--workflow', type=str, metavar='WORKFLOW', help='run with workflow file') + parser.add_argument( + '-b', '--backend', type=str, help='generate code for given backend') + # just for help message compile_group = parser.add_argument_group('compile to circle model') for tool, desc in subtool_list['compile'].items(): @@ -111,6 +115,48 @@ def _parse_arg(parser): return args +def _verify_backend_args(parser, args): + """ + verify one-profile, one-codegen arguments + + This verification logic comes from each drivers' codes. + """ + cfgparser = configparser.ConfigParser() + cfgparser.optionxform = str + cfgparser.read(args.config) + + for driver in ['one-profile', 'one-codegen']: + if not driver in cfgparser: + continue + + cfg_args = SimpleNamespace() + oneutils.parse_cfg(args.config, driver, cfg_args) + cmd_backend_exist = oneutils.is_valid_attr(args, 'backend') + cfg_backend_exist = oneutils.is_valid_attr(cfg_args, 'backend') + cfg_backends_exist = oneutils.is_valid_attr(cfg_args, 'backends') + + if cfg_backend_exist and cfg_backends_exist: + parser.error( + "'backend' option and 'backends' option cannot be used simultaneously.") + + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist and cfg_backend_exist: + if args.backend != cfg_args.backend: + parser.error('Not found the command of given backend') + + if cfg_backends_exist: + cfg_backends = getattr(cfg_args, 'backends').split(',') + # check if commands of given backends exist + for b in cfg_backends: + if not oneutils.is_valid_attr(cfg_args, b): + parser.error('Not found the command for ' + b) + + # Check if given backend from command line exists in the configuration file + if cmd_backend_exist: + if args.backend not in cfg_backends: + parser.error('Not found the command of given backend') + + def _verify_arg(parser, args): """verify given arguments""" # check if required arguments is given @@ -126,6 +172,13 @@ def _verify_arg(parser, args): if not getattr(args, 'O') in opt_name_list: parser.error('Invalid optimization option') + if oneutils.is_valid_attr(args, 'backend') and oneutils.is_valid_attr( + args, 'workflow'): + parser.error('\'backend\' option can be used only with \'config\' option') + + if oneutils.is_valid_attr(args, 'backend'): + _verify_backend_args(parser, args) + def main(): # check if there is subtool argument @@ -152,6 +205,8 @@ def main(): runner.detect_import_drivers(bin_dir) if oneutils.is_valid_attr(args, 'O'): runner.add_opt(getattr(args, 'O')) + if oneutils.is_valid_attr(args, 'backend'): + runner.set_backend(args.backend) runner.run(bin_dir) elif oneutils.is_valid_attr(args, 'workflow'): runner = WorkflowRunner(args.workflow) diff --git a/compiler/one-cmds/onelib/CfgRunner.py b/compiler/one-cmds/onelib/CfgRunner.py index fe1c41a..3a7b5fc 100644 --- a/compiler/one-cmds/onelib/CfgRunner.py +++ b/compiler/one-cmds/onelib/CfgRunner.py @@ -56,6 +56,8 @@ class CfgRunner: # add_opt receives group name except first 'O' self.add_opt(o[1:]) + self.backend = None + def _verify_cfg(self, cfgparser): if not cfgparser.has_section('onecc'): if cfgparser.has_section('one-build'): @@ -88,6 +90,9 @@ class CfgRunner: ) self.opt = opt + def set_backend(self, backend: str): + self.backend = backend + def detect_import_drivers(self, dir): self.import_drivers = list(oneutils.detect_one_import_drivers(dir).keys()) @@ -109,6 +114,8 @@ class CfgRunner: options += ['-O', self.opt] if verbose: options.append('--verbose') + if (section == 'one-codegen' or section == 'one-profile') and self.backend: + options += ['-b', self.backend] driver_path = os.path.join(working_dir, section) cmd = [driver_path] + options oneutils.run(cmd) diff --git a/compiler/one-cmds/onelib/backends.py b/compiler/one-cmds/onelib/backends.py new file mode 100644 index 0000000..9d7dad1 --- /dev/null +++ b/compiler/one-cmds/onelib/backends.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 glob +import ntpath +import os +""" +[one hierarchy] +one +├── backends +├── bin +├── doc +├── include +├── lib +├── optimization +└── test + +The list where `one-XXXX` finds its backends +- `bin` folder where `one-XXXX` exists +- `backends` folder + +NOTE If there are backends of the same name in different places, + the closer to the top in the list, the higher the priority. +""" + + +def get_list(cmdname): + dir_path = os.path.dirname(os.path.realpath(__file__)) + backend_set = set() + + # bin folder + files = [f for f in glob.glob(dir_path + '/../*-' + cmdname)] + # backends folder + files += [ + f + for f in glob.glob(dir_path + '/../../backends/**/*-' + cmdname, recursive=True) + ] + # TODO find backends in `$PATH` + + backends_list = [] + for cand in files: + base = ntpath.basename(cand) + if (not base in backend_set) and os.path.isfile(cand) and os.access( + cand, os.X_OK): + backend_set.add(base) + backends_list.append(cand) + + return backends_list + + +def search_driver(driver): + dir_path = os.path.dirname(os.path.realpath(__file__)) + + # CASE 1: one/bin/{driver} is found + driver_path = dir_path + '/../' + driver + if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): + return driver_path + + # CASE 2: one/backends/**/bin/{driver} is found + for driver_path in glob.glob( + dir_path + '/../../backends/**/bin/' + driver, recursive=True): + if os.path.isfile(driver_path) and os.access(driver_path, os.X_OK): + return driver_path + + # CASE 3: {driver} is found in nowhere + return None diff --git a/compiler/one-cmds/onelib/constant.py b/compiler/one-cmds/onelib/constant.py index 0f338fc..90109ef 100644 --- a/compiler/one-cmds/onelib/constant.py +++ b/compiler/one-cmds/onelib/constant.py @@ -40,6 +40,7 @@ class CONSTANT: 'fuse_activation_function', 'fuse_instnorm', 'fuse_prelu', + 'fuse_gelu', 'fuse_mean_with_mean', 'fuse_transpose_with_mean', 'transform_min_max_to_relu6', @@ -109,6 +110,7 @@ class CONSTANT: ('fuse_activation_function', 'fuse Activation function to a preceding operator'), ('fuse_instnorm', 'fuse ops to InstanceNorm operator'), ('fuse_prelu', 'fuse ops to PReLU operator'), + ('fuse_gelu', 'fuse ops to GeLU operator'), ('replace_cw_mul_add_with_depthwise_conv', 'replace channel-wise Mul/Add with DepthwiseConv2D'), ('remove_fakequant', 'remove FakeQuant ops'), @@ -143,7 +145,10 @@ class CONSTANT: 'convert certain condition Transpose to Reshape'), ('transform_min_max_to_relu6', 'transform Minimum-Maximum pattern to Relu6 op'), ('transform_min_relu_to_relu6', 'transform Minimum(6)-Relu pattern to Relu6 op'), - ('unroll_unidirseqlstm', 'unroll UnidirectionalSequenceLSTM op')) + ('decompose_hardswish', 'decompose the HardSwish op to Add, Mul and Relu6 ops'), + ('unroll_unidirseqlstm', 'unroll UnidirectionalSequenceLSTM op'), + ('dynamic_batch_to_single_batch', + 'convert dynamic batch size (first dimension) of inputs to 1')) CONSTANT = CONSTANT() diff --git a/compiler/one-cmds/onelib/utils.py b/compiler/one-cmds/onelib/utils.py index 3d37f9d..f7a1a96 100644 --- a/compiler/one-cmds/onelib/utils.py +++ b/compiler/one-cmds/onelib/utils.py @@ -17,7 +17,8 @@ import argparse import configparser import glob -import importlib +import importlib.machinery +import importlib.util import ntpath import os import subprocess @@ -83,29 +84,6 @@ def is_valid_attr(args, attr): return hasattr(args, attr) and getattr(args, attr) -def parse_cfg_and_overwrite(config_path, section, args): - """ - parse given section of configuration file and set the values of args. - Even if the values parsed from the configuration file already exist in args, - the values are overwritten. - """ - if config_path == None: - # DO NOTHING - return - config = configparser.ConfigParser() - # make option names case sensitive - config.optionxform = str - parsed = config.read(config_path) - if not parsed: - raise FileNotFoundError('Not found given configuration file') - if not config.has_section(section): - raise AssertionError('configuration file doesn\'t have \'' + section + - '\' section') - for key in config[section]: - setattr(args, key, config[section][key]) - # TODO support accumulated arguments - - def parse_cfg(config_path: Union[str, None], section_to_parse: str, args): """ parse configuration file and store the information to args diff --git a/compiler/one-cmds/requires.cmake b/compiler/one-cmds/requires.cmake index c279209..a25a7d7 100644 --- a/compiler/one-cmds/requires.cmake +++ b/compiler/one-cmds/requires.cmake @@ -3,6 +3,7 @@ require("tflite2circle") require("circle2circle") require("circle-eval-diff") require("circle-quantizer") +require("circle-mpqsolver") require("record-minmax") require("vconone") require("bcq-tools") diff --git a/compiler/one-cmds/tests/one-codegen_neg_001.test b/compiler/one-cmds/tests/one-codegen_neg_001.test index 27db028..137a3f9 100644 --- a/compiler/one-cmds/tests/one-codegen_neg_001.test +++ b/compiler/one-cmds/tests/one-codegen_neg_001.test @@ -21,7 +21,7 @@ filename="${filename_ext%.*}" trap_err_onexit() { - if grep -q "error: the following arguments are required" "${filename}.log"; then + if grep -q "error: the following arguments are required: -b/--backend" "${filename}.log"; then echo "${filename_ext} SUCCESS" exit 0 fi diff --git a/compiler/one-cmds/tests/one-codegen_neg_002.test b/compiler/one-cmds/tests/one-codegen_neg_002.test new file mode 100644 index 0000000..28dfa92 --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_002.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# one-codegen ${command} without backend option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required: -b/--backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-codegen -o test.tvn test.circle > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-codegen_neg_003.test b/compiler/one-cmds/tests/one-codegen_neg_003.test new file mode 100644 index 0000000..0622b6a --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_003.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# one-codegen -- ${command} without backend option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required: -b/--backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-codegen -- -o test.tvn test.circle > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-codegen_neg_004.cfg b/compiler/one-cmds/tests/one-codegen_neg_004.cfg new file mode 100644 index 0000000..b104fbb --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_004.cfg @@ -0,0 +1,6 @@ +[onecc] +one-codegen=True + +[one-codegen] +backend=dummy +# command=.. diff --git a/compiler/one-cmds/tests/one-codegen_neg_004.test b/compiler/one-cmds/tests/one-codegen_neg_004.test new file mode 100644 index 0000000..c35549b --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_004.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# command key is missing + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: 'command' key is missing in the configuration file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-codegen_neg_004.cfg" + +rm -f ${filename}.log + +# run test +one-codegen -b dummy -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-codegen_neg_005.test b/compiler/one-cmds/tests/one-codegen_neg_005.test new file mode 100644 index 0000000..4c52dcd --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_005.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# commands for the backend is missing + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: commands for the backend is missing" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-codegen -b dummy > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-import-onnx_ext_001.test b/compiler/one-cmds/tests/one-import-onnx_ext_001.test new file mode 100644 index 0000000..be8dadc --- /dev/null +++ b/compiler/one-cmds/tests/one-import-onnx_ext_001.test @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# test for one-import-onnx to invoke extension + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -f ../bin/one-import-onnx-ext + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./onnx_conv2d_conv2d.onnx" +outputfile="./onnx_conv2d_conv2d.onnx_ext_001.circle" +logfile=${filename}.log + +rm -f ${outputfile} +rm -f ${logfile} + +# copy dummy-compile to bin folder +cp dummy-onnx-ext ../bin/one-import-onnx-ext + +# run test +one-import-onnx \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${logfile} 2>&1 + +if ! grep -q "one-import-onnx-ext dummy output!!!" "${logfile}"; then + trap_err_onexit +fi + +rm -f ../bin/one-import-onnx-ext + +echo "${filename_ext} SUCCESS" +exit 0 diff --git a/compiler/one-cmds/tests/one-import_neg_001.test b/compiler/one-cmds/tests/one-import_neg_001.test index 82a3e33..20a6964 100644 --- a/compiler/one-cmds/tests/one-import_neg_001.test +++ b/compiler/one-cmds/tests/one-import_neg_001.test @@ -25,6 +25,11 @@ trap_err_onexit() echo "${filename_ext} SUCCESS" exit 0 fi + # TF 2.12.x: error report has changed + if grep -q "invalid start byte" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi echo "${filename_ext} FAILED" exit 255 diff --git a/compiler/one-cmds/tests/one-profile_neg_002.test b/compiler/one-cmds/tests/one-profile_neg_002.test new file mode 100644 index 0000000..6964312 --- /dev/null +++ b/compiler/one-cmds/tests/one-profile_neg_002.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# one-profile ${command} without backend option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required: -b/--backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-profile test.tvn > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-profile_neg_003.test b/compiler/one-cmds/tests/one-profile_neg_003.test new file mode 100644 index 0000000..a002154 --- /dev/null +++ b/compiler/one-cmds/tests/one-profile_neg_003.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# one-profile -- ${command} without backend option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required: -b/--backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-profile -- test.tvn > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-profile_neg_004.cfg b/compiler/one-cmds/tests/one-profile_neg_004.cfg new file mode 100644 index 0000000..93afcb4 --- /dev/null +++ b/compiler/one-cmds/tests/one-profile_neg_004.cfg @@ -0,0 +1,6 @@ +[onecc] +one-profile=True + +[one-profile] +backend=dummy +# command=.. diff --git a/compiler/one-cmds/tests/one-profile_neg_004.test b/compiler/one-cmds/tests/one-profile_neg_004.test new file mode 100644 index 0000000..404ac10 --- /dev/null +++ b/compiler/one-cmds/tests/one-profile_neg_004.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# command key is missing + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: 'command' key is missing in the configuration file" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-profile_neg_004.cfg" + +rm -f ${filename}.log + +# run test +one-profile -b dummy -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-profile_neg_005.test b/compiler/one-cmds/tests/one-profile_neg_005.test new file mode 100644 index 0000000..9a91549 --- /dev/null +++ b/compiler/one-cmds/tests/one-profile_neg_005.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# commands for the backend is missing + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: commands for the backend is missing" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +rm -f ${filename}.log + +# run test +one-profile -b dummy > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-quantize_016.test b/compiler/one-cmds/tests/one-quantize_016.test index 8bf0dd0..cfebc2f 100644 --- a/compiler/one-cmds/tests/one-quantize_016.test +++ b/compiler/one-cmds/tests/one-quantize_016.test @@ -54,8 +54,8 @@ if [[ ! -s "${outputfile}" ]]; then trap_err_onexit fi -circledump ${outputfile} | grep serving_default_l.1:0$ > ${filename}.first.cdump -circledump ${outputfile} | grep serving_default_r.1:0$ > ${filename}.second.cdump +circledump ${outputfile} | grep "T(0:0)" > ${filename}.first.cdump +circledump ${outputfile} | grep "T(0:1)" > ${filename}.second.cdump # check dtype of the first input (uint8) if ! grep -q "UINT8" "${filename}.first.cdump"; then diff --git a/compiler/one-cmds/tests/one-quantize_017.test b/compiler/one-cmds/tests/one-quantize_017.test new file mode 100644 index 0000000..f4d3ee8 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_017.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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="./mobilenet_edgetpu_224_1.0_int8.circle" +outputfile="./mobilenet_edgetpu_224_1.0_int8.one-quantize_017.circle" + +rm -f ${filename}.log +rm -f ${outputfile} + +# run test +one-quantize \ +--requantize \ +--input_model_dtype int8 \ +--quantized_dtype uint8 \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_018.test b/compiler/one-cmds/tests/one-quantize_018.test new file mode 100644 index 0000000..dd609dd --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_018.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ampq test for bisection_type = 'i16_front' (front nodes will be quantized to int16) +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.q_opt.one-quantize_018.circle" +datafile="./inception_v3_test_data.h5" +bisection_type="i16_front" + +rm -f ${filename}.log +rm -f ${outputfile} + +# run test +one-quantize \ +--input_data ${datafile} \ +--input_path ${inputfile} \ +--ampq \ +--ampq_qerror_ratio "0.5" \ +--ampq_algorithm "bisection" \ +--bisection_type ${bisection_type} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_019.test b/compiler/one-cmds/tests/one-quantize_019.test new file mode 100644 index 0000000..3001fad --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_019.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ampq test for bisection_type = 'i16_back' (output nodes will be quantized to int16) +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.q_opt.one-quantize_019.circle" +datafile="./inception_v3_test_data.h5" +bisection_type="i16_back" + +rm -f ${filename}.log +rm -f ${outputfile} + +# run test +one-quantize \ +--input_data ${datafile} \ +--input_path ${inputfile} \ +--ampq \ +--ampq_qerror_ratio "0.5" \ +--ampq_algorithm "bisection" \ +--bisection_type ${bisection_type} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_020.test b/compiler/one-cmds/tests/one-quantize_020.test new file mode 100644 index 0000000..5167295 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_020.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ampq test with bisection_type set to auto +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.q_opt.one-quantize_020.circle" +datafile="./inception_v3_test_data.h5" +bisection_type="auto" + +rm -f ${filename}.log +rm -f ${outputfile} + +# run test +one-quantize \ +--input_data ${datafile} \ +--input_path ${inputfile} \ +--ampq \ +--ampq_qerror_ratio "0.5" \ +--ampq_algorithm "bisection" \ +--bisection_type ${bisection_type} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_021.test b/compiler/one-cmds/tests/one-quantize_021.test new file mode 100644 index 0000000..b6abc66 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_021.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test moving average parameters (moving average per Image) +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.one-quantize_021.circle" + +rm -f ${filename}.log +rm -f ${outputfile} + +# run test +one-quantize \ +--input_path ${inputfile} \ +--mode moving_average \ +--moving_avg_batch 1 \ +--moving_avg_const 0.01 \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-quantize_neg_003.test b/compiler/one-cmds/tests/one-quantize_neg_003.test index e8b9648..af2db72 100644 --- a/compiler/one-cmds/tests/one-quantize_neg_003.test +++ b/compiler/one-cmds/tests/one-quantize_neg_003.test @@ -21,7 +21,7 @@ filename="${filename_ext%.*}" trap_err_onexit() { - if grep -q "Input shape mismatch" "${filename}.log"; then + if grep -q "Buffer size does not match" "${filename}.log"; then echo "${filename_ext} SUCCESS" exit 0 fi diff --git a/compiler/one-cmds/tests/one-quantize_neg_022.test b/compiler/one-cmds/tests/one-quantize_neg_022.test new file mode 100644 index 0000000..5193941 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_neg_022.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Wrong number of input_type in one-quantize + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "following arguments are required: --input_model_dtype" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./mobilenet_edgetpu_224_1.0_int8.circle" +outputfile="./mobilenet_edgetpu_224_1.0_int8.one-quantize_neg_022.circle" + +rm -f ${filename}.log + +# run test with wrong input model dtype +one-quantize \ +--requantize \ +--quantized_dtype uint8 \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-quantize_neg_023.test b/compiler/one-cmds/tests/one-quantize_neg_023.test new file mode 100644 index 0000000..7ed97c18c --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_neg_023.test @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Wrong type of calibration parameter + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "moving_avg_batch must be integer" "${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.one-quantize_neg_023.circle" + +rm -f ${filename}.log + +# run test with wrong parameter dtype +# moving_avg_batch must be integer +one-quantize \ +--input_path ${inputfile} \ +--mode moving_average \ +--moving_avg_batch 0.1 \ +--output_path ${outputfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_045.test b/compiler/one-cmds/tests/onecc_045.test index 74e21f6..2b5c0a2 100644 --- a/compiler/one-cmds/tests/onecc_045.test +++ b/compiler/one-cmds/tests/onecc_045.test @@ -49,8 +49,8 @@ if [[ ! -s "${outputfile}" ]]; then trap_err_onexit fi -circledump ${outputfile} | grep serving_default_l.1:0$ > ${filename}.first.cdump -circledump ${outputfile} | grep serving_default_r.1:0$ > ${filename}.second.cdump +circledump ${outputfile} | grep "T(0:0)" > ${filename}.first.cdump +circledump ${outputfile} | grep "T(0:1)" > ${filename}.second.cdump # check dtype of the first input (uint8) if ! grep -q "UINT8" "${filename}.first.cdump"; then diff --git a/compiler/one-cmds/tests/onecc_046.cfg b/compiler/one-cmds/tests/onecc_046.cfg new file mode 100644 index 0000000..2892735 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_046.cfg @@ -0,0 +1,7 @@ +[onecc] +one-codegen=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o onecc_046.tvn inception_v3.onecc_046.circle +dummyV2=-O onecc_046.2.tvn inception_v3.onecc_046.2.circle diff --git a/compiler/one-cmds/tests/onecc_046.test b/compiler/one-cmds/tests/onecc_046.test new file mode 100644 index 0000000..a11b181 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_046.test @@ -0,0 +1,58 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backends' key in configuration file + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_046.cfg" +outputfile="onecc_046.tvn" +outputfile2="onecc_046.2.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile} +rm -rf ${outputfile2} + +# copy dummy tools to bin folder +cp dummy-compile ../bin/dummy-compile +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +if [[ ! -s "${outputfile2}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_047.cfg b/compiler/one-cmds/tests/onecc_047.cfg new file mode 100644 index 0000000..df2eef5 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_047.cfg @@ -0,0 +1,7 @@ +[onecc] +one-codegen=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o onecc_047.tvn inception_v3.onecc_047.circle +dummyV2=-O onecc_047.2.tvn inception_v3.onecc_047.2.circle diff --git a/compiler/one-cmds/tests/onecc_047.test b/compiler/one-cmds/tests/onecc_047.test new file mode 100644 index 0000000..3450420 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_047.test @@ -0,0 +1,59 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backends' key in configuration file but run codegen for only one backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_047.cfg" +outputfile="onecc_047.tvn" +outputfile2="onecc_047.2.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile} +rm -rf ${outputfile2} + +# copy dummy tools to bin folder +cp dummy-compile ../bin/dummy-compile +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc -C ${configfile} -b dummyV2 > ${filename}.log 2>&1 + +# shouldn't be generated +if [[ -s "${outputfile}" ]]; then + trap_err_onexit +fi + +if [[ ! -s "${outputfile2}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_048.cfg b/compiler/one-cmds/tests/onecc_048.cfg new file mode 100644 index 0000000..c4c0acc --- /dev/null +++ b/compiler/one-cmds/tests/onecc_048.cfg @@ -0,0 +1,6 @@ +[onecc] +one-codegen=True + +[one-codegen] +backend=dummyV2 +command=-O onecc_048.tvn inception_v3.onecc_048.circle diff --git a/compiler/one-cmds/tests/onecc_048.test b/compiler/one-cmds/tests/onecc_048.test new file mode 100644 index 0000000..d4eff54 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_048.test @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backend' option from command line with 'backend' key in configuration file + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_048.cfg" +outputfile="onecc_048.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile} + +# copy dummy tools to bin folder +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc -C ${configfile} -b dummyV2 > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_049.cfg b/compiler/one-cmds/tests/onecc_049.cfg new file mode 100644 index 0000000..021d9a4 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_049.cfg @@ -0,0 +1,7 @@ +[onecc] +one-profile=True + +[one-profile] +backends=dummy,dummyV2 +dummy=dummy.bin +dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_049.test b/compiler/one-cmds/tests/onecc_049.test new file mode 100644 index 0000000..45fd8a7 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_049.test @@ -0,0 +1,54 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backends' key in one-profile section + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + rm -rf ../bin/dummyV2-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_049.cfg" + +rm -f ${filename}.log + +# copy dummy tools to bin folder +cp dummy-profile ../bin/dummy-profile +cp dummyV2-profile ../bin/dummyV2-profile + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +if ! grep -q "dummy-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if ! grep -q "dummyV2-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-profile +rm -rf ../bin/dummyV2-profile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_050.cfg b/compiler/one-cmds/tests/onecc_050.cfg new file mode 100644 index 0000000..021d9a4 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_050.cfg @@ -0,0 +1,7 @@ +[onecc] +one-profile=True + +[one-profile] +backends=dummy,dummyV2 +dummy=dummy.bin +dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_050.test b/compiler/one-cmds/tests/onecc_050.test new file mode 100644 index 0000000..8ebfd1e --- /dev/null +++ b/compiler/one-cmds/tests/onecc_050.test @@ -0,0 +1,54 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backends' key in configuration file but run one-profile for only one backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + rm -rf ../bin/dummyV2-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_050.cfg" + +rm -f ${filename}.log + +# copy dummy tools to bin folder +cp dummy-profile ../bin/dummy-profile +cp dummyV2-profile ../bin/dummyV2-profile + +# run test +onecc -C ${configfile} -b dummyV2 > ${filename}.log 2>&1 + +if grep -q "dummy-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if ! grep -q "dummyV2-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-profile +rm -rf ../bin/dummyV2-profile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_051.cfg b/compiler/one-cmds/tests/onecc_051.cfg new file mode 100644 index 0000000..ecf983f --- /dev/null +++ b/compiler/one-cmds/tests/onecc_051.cfg @@ -0,0 +1,6 @@ +[onecc] +one-profile=True + +[one-profile] +backend=dummyV2 +command=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_051.test b/compiler/one-cmds/tests/onecc_051.test new file mode 100644 index 0000000..9796709 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_051.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backend' option from command line with 'backend' key in configuration file + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + rm -rf ../bin/dummyV2-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_051.cfg" + +rm -f ${filename}.log + +# copy dummy tools to bin folder +cp dummyV2-profile ../bin/dummyV2-profile + +# run test +onecc -C ${configfile} -b dummyV2 > ${filename}.log 2>&1 + +if ! grep -q "dummyV2-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +rm -rf ../bin/dummyV2-profile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_052.cfg b/compiler/one-cmds/tests/onecc_052.cfg new file mode 100644 index 0000000..e884ea9 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_052.cfg @@ -0,0 +1,13 @@ +[onecc] +one-codegen=True +one-profile=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o onecc_052.tvn inception_v3.onecc_052.circle +dummyV2=-O onecc_052.2.tvn inception_v3.onecc_052.2.circle + +[one-profile] +backends=dummy,dummyV2 +dummy=dummy.bin +dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_052.test b/compiler/one-cmds/tests/onecc_052.test new file mode 100644 index 0000000..39bc6ed --- /dev/null +++ b/compiler/one-cmds/tests/onecc_052.test @@ -0,0 +1,74 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backends' key with one-profile and one-codgen section + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + rm -rf ../bin/dummyV2-profile + rm -rf ../bin/dummy-compile + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_052.cfg" +outputfile="onecc_052.tvn" +outputfile2="onecc_052.2.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile} +rm -rf ${outputfile2} + +# copy dummy tools to bin folder +cp dummy-profile ../bin/dummy-profile +cp dummyV2-profile ../bin/dummyV2-profile +cp dummy-compile ../bin/dummy-compile +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +if ! grep -q "dummy-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if ! grep -q "dummyV2-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if [[ ! -s "${outputfile}" ]]; then + echo "ERROR: Not found ${outputfile}" >> ${filename}.log + trap_err_onexit +fi + +if [[ ! -s "${outputfile2}" ]]; then +echo "ERROR: Not found ${outputfile2}" >> ${filename}.log + trap_err_onexit +fi + +rm -rf ../bin/dummy-profile +rm -rf ../bin/dummyV2-profile +rm -rf ../bin/dummy-compile +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_053.cfg b/compiler/one-cmds/tests/onecc_053.cfg new file mode 100644 index 0000000..8cbe799 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_053.cfg @@ -0,0 +1,13 @@ +[onecc] +one-codegen=True +one-profile=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o onecc_053.tvn inception_v3.onecc_053.circle +dummyV2=-O onecc_053.2.tvn inception_v3.onecc_053.2.circle + +[one-profile] +backends=dummy,dummyV2 +dummy=dummy.bin +dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_053.test b/compiler/one-cmds/tests/onecc_053.test new file mode 100644 index 0000000..20f0209 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_053.test @@ -0,0 +1,74 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# 'backend' option with one-profile and one-codgen section + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-profile + rm -rf ../bin/dummyV2-profile + rm -rf ../bin/dummy-compile + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_053.cfg" +outputfile="onecc_053.tvn" +outputfile2="onecc_053.2.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile} +rm -rf ${outputfile2} + +# copy dummy tools to bin folder +cp dummy-profile ../bin/dummy-profile +cp dummyV2-profile ../bin/dummyV2-profile +cp dummy-compile ../bin/dummy-compile +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc -C ${configfile} -b dummyV2 > ${filename}.log 2>&1 + +if grep -q "dummy-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if ! grep -q "dummyV2-profile dummy output!!!" "${filename}.log"; then + trap_err_onexit +fi + +if [[ -s "${outputfile}" ]]; then + echo "ERROR: Found ${outputfile}" >> ${filename}.log + trap_err_onexit +fi + +if [[ ! -s "${outputfile2}" ]]; then +echo "ERROR: Not found ${outputfile2}" >> ${filename}.log + trap_err_onexit +fi + +rm -rf ../bin/dummy-profile +rm -rf ../bin/dummyV2-profile +rm -rf ../bin/dummy-compile +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_054.cfg b/compiler/one-cmds/tests/onecc_054.cfg new file mode 100644 index 0000000..63ad06c --- /dev/null +++ b/compiler/one-cmds/tests/onecc_054.cfg @@ -0,0 +1,7 @@ +[onecc] +one-codegen=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o onecc_054.tvn inception_v3.onecc_054.circle +dummyV2=-O onecc_054.2.tvn inception_v3.onecc_054.2.circle diff --git a/compiler/one-cmds/tests/onecc_054.test b/compiler/one-cmds/tests/onecc_054.test new file mode 100644 index 0000000..14853a8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_054.test @@ -0,0 +1,65 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# overwrite one-codegen command with `backends` key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_054.cfg" +outputfile0="onecc_054_overwrite.tvn" +outputfile1="onecc_054.tvn" +outputfile2="onecc_054.2.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile0} +rm -rf ${outputfile1} +rm -rf ${outputfile2} + +# copy dummy tools to bin folder +cp dummy-compile ../bin/dummy-compile +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc codegen -C ${configfile} -b dummyV2 -- \ + -O onecc_054_overwrite.tvn onecc.circle > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile0}" ]]; then + trap_err_onexit +fi + +# shouldn't be generated +if [[ -s "${outputfile1}" ]]; then + trap_err_onexit +fi +if [[ -s "${outputfile2}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_055.cfg b/compiler/one-cmds/tests/onecc_055.cfg new file mode 100644 index 0000000..c4c0acc --- /dev/null +++ b/compiler/one-cmds/tests/onecc_055.cfg @@ -0,0 +1,6 @@ +[onecc] +one-codegen=True + +[one-codegen] +backend=dummyV2 +command=-O onecc_048.tvn inception_v3.onecc_048.circle diff --git a/compiler/one-cmds/tests/onecc_055.test b/compiler/one-cmds/tests/onecc_055.test new file mode 100644 index 0000000..c13aa4b --- /dev/null +++ b/compiler/one-cmds/tests/onecc_055.test @@ -0,0 +1,57 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# overwrite one-codegen command with `backend` and `command` key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummyV2-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_055.cfg" +outputfile0="onecc_055.tvn" +outputfile1="onecc_055_overwrite.tvn" + +rm -f ${filename}.log +rm -rf ${outputfile0} +rm -rf ${outputfile1} + +# copy dummy tools to bin folder +cp dummyV2-compile ../bin/dummyV2-compile + +# run test +onecc codegen -C ${configfile} -b dummyV2 -- \ + -O onecc_055_overwrite.tvn onecc.circle > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile1}" ]]; then + trap_err_onexit +fi + +# shouldn't be generated +if [[ -s "${outputfile0}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummyV2-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_056.cfg b/compiler/one-cmds/tests/onecc_056.cfg new file mode 100644 index 0000000..1d30fd7 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_056.cfg @@ -0,0 +1,6 @@ +[onecc] +one-profile=True + +[one-profile] +backend=dummyV3 +command=onecc_056 diff --git a/compiler/one-cmds/tests/onecc_056.test b/compiler/one-cmds/tests/onecc_056.test new file mode 100644 index 0000000..159cafb --- /dev/null +++ b/compiler/one-cmds/tests/onecc_056.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# overwrite one-profile command with 'backend' key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummyV3-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_056.cfg" + +rm -f ${filename}.log + +# copy dummy tools to bin folder +cp dummyV3-profile ../bin/dummyV3-profile + +# run test +onecc profile -C ${configfile} -b dummyV3 -- \ + onecc_056_overwrite > ${filename}.log 2>&1 + +if ! grep -q "dummyV3-profile with onecc_056_overwrite" "${filename}.log"; then + trap_err_onexit +fi + +rm -rf ../bin/dummyV3-profile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_057.cfg b/compiler/one-cmds/tests/onecc_057.cfg new file mode 100644 index 0000000..dbe4f53 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_057.cfg @@ -0,0 +1,7 @@ +[onecc] +one-profile=True + +[one-profile] +backends=dummyV2,dummyV3 +dummyV2=dummyV2.bin +dummyV3=onecc_057 diff --git a/compiler/one-cmds/tests/onecc_057.test b/compiler/one-cmds/tests/onecc_057.test new file mode 100644 index 0000000..f007609 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_057.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# overwrite one-profile command with `backends` key + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummyV2-profile + rm -rf ../bin/dummyV3-profile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_057.cfg" + +rm -f ${filename}.log + +# copy dummy tools to bin folder +cp dummyV2-profile ../bin/dummyV2-profile +cp dummyV3-profile ../bin/dummyV3-profile + +# run test +onecc profile -C ${configfile} -b dummyV3 -- \ + onecc_057_overwrite > ${filename}.log 2>&1 + +if ! grep -q "dummyV3-profile with onecc_057_overwrite" "${filename}.log"; then + trap_err_onexit +fi + +rm -rf ../bin/dummyV2-profile +rm -rf ../bin/dummyV3-profile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_058.cfg b/compiler/one-cmds/tests/onecc_058.cfg new file mode 100644 index 0000000..1d0b6d1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_058.cfg @@ -0,0 +1,9 @@ +[onecc] +one-quantize=True + +[one-quantize] +requantize=True +input_path=mobilenet_edgetpu_224_1.0_int8.circle +output_path=mobilenet_edgetpu_224_1.0_int8.onecc_058.circle +input_model_dtype=int8 +quantized_dtype=uint8 diff --git a/compiler/one-cmds/tests/onecc_058.test b/compiler/one-cmds/tests/onecc_058.test new file mode 100644 index 0000000..6f60eed --- /dev/null +++ b/compiler/one-cmds/tests/onecc_058.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 + +configfile="onecc_058.cfg" +outputfile="mobilenet_edgetpu_224_1.0_int8.onecc_058.circle" + +rm -f ${filename}.log +rm -rf ${outputfile} + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_neg_027.cfg b/compiler/one-cmds/tests/onecc_neg_027.cfg new file mode 100644 index 0000000..27f3f67 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_027.cfg @@ -0,0 +1,7 @@ +[onecc] +one-codegen=True + +[one-codegen] +backends=dummy,dummyV2 +dummy=-o sample.tvn inception_v3.onecc_neg_027.circle +# dummyV2=-O sample2.tvn inception_v3.onecc_neg_027.circle diff --git a/compiler/one-cmds/tests/onecc_neg_027.test b/compiler/one-cmds/tests/onecc_neg_027.test new file mode 100644 index 0000000..f968256 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_027.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command for given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command for dummyV2" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_027.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_028.cfg b/compiler/one-cmds/tests/onecc_neg_028.cfg new file mode 100644 index 0000000..ca19c53 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_028.cfg @@ -0,0 +1,9 @@ +[onecc] +one-codegen=True + +[one-codegen] +backend=dummy3 +command=-o sample.tvn inception_v3.onecc_neg_028.circle +backends=dummy,dummy2 +dummy=-o sample.tvn inception_v3.onecc_neg_028.circle +dummyV2=-O sample2.tvn inception_v3.onecc_neg_028.circle diff --git a/compiler/one-cmds/tests/onecc_neg_028.test b/compiler/one-cmds/tests/onecc_neg_028.test new file mode 100644 index 0000000..beba24a --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_028.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# use 'backend' and 'backends' option simultaneously + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "option cannot be used simultaneously" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_028.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_029.cfg b/compiler/one-cmds/tests/onecc_neg_029.cfg new file mode 100644 index 0000000..02f527b --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_029.cfg @@ -0,0 +1,6 @@ +[onecc] +one-codegen=True + +[one-codegen] +backend=dummy3 +command=-o sample.tvn inception_v3.onecc_neg_029.circle diff --git a/compiler/one-cmds/tests/onecc_neg_029.test b/compiler/one-cmds/tests/onecc_neg_029.test new file mode 100644 index 0000000..5ced5fe --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_029.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command of given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command of given backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_029.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} --backend dummy2 > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_030.cfg b/compiler/one-cmds/tests/onecc_neg_030.cfg new file mode 100644 index 0000000..0d573c1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_030.cfg @@ -0,0 +1,6 @@ +[onecc] +one-codegen=True + +[one-codegen] +backends=dummy3 +dummy3=-o sample.tvn inception_v3.onecc_neg_030.circle diff --git a/compiler/one-cmds/tests/onecc_neg_030.test b/compiler/one-cmds/tests/onecc_neg_030.test new file mode 100644 index 0000000..404a9b8 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_030.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command of given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command of given backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_030.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} --backend dummy2 > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_031.test b/compiler/one-cmds/tests/onecc_neg_031.test new file mode 100644 index 0000000..c7d8575 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_031.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# use 'backend' option with 'workflow' option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "'backend' option can be used only with 'config' option" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_031.workflow.json" + +rm -f ${filename}.log + +# run test +onecc -W ${workflowfile} --backend dummy > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_031.workflow.json b/compiler/one-cmds/tests/onecc_neg_031.workflow.json new file mode 100644 index 0000000..d323cc9 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_031.workflow.json @@ -0,0 +1,29 @@ +{ + "workflows": [ + "codegen_wf" + ], + "codegen_wf": { + "steps": [ + "import_tf", + "codegen" + ], + "import_tf": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.onecc_neg_031.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "codegen": { + "one-cmd": "one-codegen", + "commands": { + "backend": "dummy", + "command": "-o sample.tvn inception_v3.onecc_neg_031.circle" + } + } + } +} diff --git a/compiler/one-cmds/tests/onecc_neg_032.cfg b/compiler/one-cmds/tests/onecc_neg_032.cfg new file mode 100644 index 0000000..9491948 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_032.cfg @@ -0,0 +1,7 @@ +[onecc] +one-profile=True + +[one-profile] +backends=dummy,dummyV2 +dummy=dummy.bin +# dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_neg_032.test b/compiler/one-cmds/tests/onecc_neg_032.test new file mode 100644 index 0000000..12b70cb --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_032.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command for given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command for dummyV2" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_032.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_033.cfg b/compiler/one-cmds/tests/onecc_neg_033.cfg new file mode 100644 index 0000000..6c77870 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_033.cfg @@ -0,0 +1,9 @@ +[onecc] +one-profile=True + +[one-profile] +backend=dummyV3 +command=dummyV3.bin +backends=dummy,dummyV2 +dummy=dummy.bin +dummyV2=dummyB2.bin diff --git a/compiler/one-cmds/tests/onecc_neg_033.test b/compiler/one-cmds/tests/onecc_neg_033.test new file mode 100644 index 0000000..51b7ca0 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_033.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# use 'backend' and 'backends' option simultaneously + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "option cannot be used simultaneously" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_033.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_034.cfg b/compiler/one-cmds/tests/onecc_neg_034.cfg new file mode 100644 index 0000000..8c64628 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_034.cfg @@ -0,0 +1,6 @@ +[onecc] +one-profile=True + +[one-profile] +backend=dummy +command=dummy.bin diff --git a/compiler/one-cmds/tests/onecc_neg_034.test b/compiler/one-cmds/tests/onecc_neg_034.test new file mode 100644 index 0000000..58cc8d3 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_034.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command of given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command of given backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_034.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} --backend dummyV2 > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_035.cfg b/compiler/one-cmds/tests/onecc_neg_035.cfg new file mode 100644 index 0000000..6468d73 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_035.cfg @@ -0,0 +1,6 @@ +[onecc] +one-profile=True + +[one-profile] +backends=dummyV2 +dummyV2=dummyV2.bin diff --git a/compiler/one-cmds/tests/onecc_neg_035.test b/compiler/one-cmds/tests/onecc_neg_035.test new file mode 100644 index 0000000..cae5ba1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_035.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# not found the command of given backend + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "Not found the command of given backend" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="onecc_neg_035.cfg" + +rm -f ${filename}.log + +# run test +onecc -C ${configfile} --backend dummy > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_036.test b/compiler/one-cmds/tests/onecc_neg_036.test new file mode 100644 index 0000000..9294575 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_036.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# use 'backend' option with 'workflow' option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "'backend' option can be used only with 'config' option" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +workflowfile="onecc_neg_036.workflow.json" + +rm -f ${filename}.log + +# run test +onecc -W ${workflowfile} --backend dummy > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/onecc_neg_036.workflow.json b/compiler/one-cmds/tests/onecc_neg_036.workflow.json new file mode 100644 index 0000000..9acd4d2 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_neg_036.workflow.json @@ -0,0 +1,29 @@ +{ + "workflows": [ + "profile_wf" + ], + "profile_wf": { + "steps": [ + "import_tf", + "profile" + ], + "import_tf": { + "one-cmd": "one-import-tf", + "commands": { + "input_path": "inception_v3.pb", + "output_path": "inception_v3.onecc_neg_036.circle", + "input_arrays": "input", + "input_shapes": "1,299,299,3", + "output_arrays": "InceptionV3/Predictions/Reshape_1", + "converter_version": "v2" + } + }, + "profile": { + "one-cmd": "one-profile", + "commands": { + "backend": "dummy", + "command": "dummy.bin" + } + } + } +} diff --git a/compiler/one-cmds/tests/prepare_test_materials.sh b/compiler/one-cmds/tests/prepare_test_materials.sh index 97bc6eb..915beff 100644 --- a/compiler/one-cmds/tests/prepare_test_materials.sh +++ b/compiler/one-cmds/tests/prepare_test_materials.sh @@ -25,6 +25,10 @@ if [[ ! -s "inception_v3.pb" ]]; then tar zxvf inception_v3_2018_04_27.tgz fi +if [[ ! -s "mobilenet_edgetpu_224_1.0_int8.tflite" ]]; then + wget -nv https://github.com/mlcommons/mobile_models/raw/main/v0_7/tflite/mobilenet_edgetpu_224_1.0_int8.tflite +fi + if [[ ! -s "while_3.pbtxt" ]]; then rm -rf while_3.zip wget -nv https://github.com/Samsung/ONE/files/5095630/while_3.zip @@ -93,9 +97,9 @@ fi if [[ ! -s "reshape_matmul.onnx" ]]; then rm -rf reshape_matmul.zip - wget -nv https://github.com/Samsung/ONE/files/9082878/reshape_matmul.zip + wget -nv https://github.com/Samsung/ONE/files/12358217/reshape_matmul.zip unzip reshape_matmul.zip - # https://github.com/Samsung/ONE/issues/9405#issuecomment-1180198137 + # https://github.com/Samsung/ONE/issues/9405#issuecomment-1680322410 fi # prepare 'reshape_matmul.circle' file used for tests @@ -175,4 +179,14 @@ if [[ ! -s ${outputfile} ]]; then --output_path ${outputfile} fi +# prepare 'mobilenet_edgetpu_224_1.0_int8.circle' file used for requantization test +inputfile="./mobilenet_edgetpu_224_1.0_int8.tflite" +outputfile="./mobilenet_edgetpu_224_1.0_int8.circle" + +if [[ ! -s ${outputfile} ]]; then + ../bin/one-import-tflite \ + --input_path ${inputfile} \ + --output_path ${outputfile} +fi + popd > /dev/null diff --git a/compiler/one-cmds/tests/preprocess_images.py b/compiler/one-cmds/tests/preprocess_images.py index 9999158..ced6e3a 100644 --- a/compiler/one-cmds/tests/preprocess_images.py +++ b/compiler/one-cmds/tests/preprocess_images.py @@ -26,8 +26,11 @@ for (root, _, files) in os.walk(input_dir): datalist = open(list_file, 'w') for f in files: with PIL.Image.open(root + '/' + f) as image: - img = np.array(image.resize((299, 299), - PIL.Image.ANTIALIAS)).astype(np.float32) + # To handle ANTIALIAS deprecation + ANTIALIAS = PIL.Image.Resampling.LANCZOS if hasattr( + PIL.Image, "Resampling") else PIL.Image.ANTIALIAS + + img = np.array(image.resize((299, 299), ANTIALIAS)).astype(np.float32) img = ((img / 255) - 0.5) * 2.0 output_file = output_dir + '/' + f.replace('jpg', 'data') img.tofile(output_file) diff --git a/compiler/one-cmds/tests/pytorch-operations/example_generator.py b/compiler/one-cmds/tests/pytorch-operations/example_generator.py index 20a80c8..2e3cdcf 100644 --- a/compiler/one-cmds/tests/pytorch-operations/example_generator.py +++ b/compiler/one-cmds/tests/pytorch-operations/example_generator.py @@ -17,7 +17,8 @@ # PyTorch Example manager import torch -import importlib +import importlib.machinery +import importlib.util import argparse import os diff --git a/compiler/onecc-docker/docker/Dockerfile b/compiler/onecc-docker/docker/Dockerfile index 9752985..ae91279 100644 --- a/compiler/onecc-docker/docker/Dockerfile +++ b/compiler/onecc-docker/docker/Dockerfile @@ -19,8 +19,8 @@ ARG VERSION RUN apt-get update && apt-get install -qqy --no-install-recommends \ wget \ ca-certificates \ - && wget https://github.com/Samsung/ONE/releases/download/${VERSION}/one-compiler_${VERSION}_amd64.deb \ - && apt-get install -y ./one-compiler_${VERSION}_amd64.deb \ + && wget --no-check-certificate https://github.com/Samsung/ONE/releases/download/${VERSION}/one-compiler-bionic_${VERSION}_amd64.deb \ + && apt-get install -y ./one-compiler-bionic_${VERSION}_amd64.deb \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["onecc"] diff --git a/compiler/onecc-docker/onecc-docker b/compiler/onecc-docker/onecc-docker index ae3b31c..c68c7f1 100644 --- a/compiler/onecc-docker/onecc-docker +++ b/compiler/onecc-docker/onecc-docker @@ -16,17 +16,52 @@ import sys import subprocess -import json import requests import os import argparse +import re -def _run(cmd, is_shell=False): +class RequestHandler: + def __init__(self, token=None, timeout=None): + if token: + self.headers = {"Authorization": "Bearer {}".format(token)} + else: + self.headers = {} + self.timeout = timeout or 5 + + def make_request(self, url): + try: + response = requests.get(url, headers=self.headers, timeout=self.timeout) + response.raise_for_status() + return response + except requests.RequestException as e: + raise SystemExit('[onecc-docker] error: {}'.format(e)) + + +# 5 sec timeout is set based on github.com/Samsung/ONE/issues/11134 +def _request_recent_version(token=None): + response = RequestHandler( + token, + timeout=5).make_request(url="https://api.github.com/repos/Samsung/ONE/releases") + versions = [release_item["tag_name"] for release_item in response.json()] + + for version in versions: + # Return the latest version with the given format + # to filter out such as 'onert-micro-0.1.0' release + # which doesn't contain onecc package. + if bool(re.match(r'^\d+\.\d+\.\d+$', version)): + return version + + raise SystemExit('[onecc-docker] Failed to get latest onecc version') + + +# 10 sec timeout is set based on github.com/Samsung/ONE/issues/11134 +def _run(cmd, is_shell=False, timeout=10): result = subprocess.Popen( cmd, shell=is_shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = result.communicate() + stdout, stderr = result.communicate(timeout=timeout) stdout = stdout.decode('utf-8') stderr = stderr.decode('utf-8') @@ -61,20 +96,7 @@ def main(): args, onecc_arguments = parser.parse_known_args() authorization_token = args.token - LATEST_URL = "https://api.github.com/repos/Samsung/ONE/releases/latest" - headers = {} - if authorization_token: - headers = {"Authorization": "Bearer {}".format(authorization_token)} - try: - response = requests.get(LATEST_URL, headers=headers) - response.raise_for_status() - except requests.exceptions.RequestException as e: - raise SystemExit('onecc-docker: error: {}'.format(e)) - - versions_str = response.content - versions_json = json.loads(versions_str) - recent_version = versions_json["tag_name"] - + recent_version = _request_recent_version(authorization_token) image_name = f"onecc:{recent_version}" build_arg = f"VERSION={recent_version}" @@ -82,9 +104,9 @@ def main(): build_cmd = [ "docker", "build", "-t", image_name, "--build-arg", build_arg, dockerfile_path ] - print('build Docker image ...') - _run(build_cmd) - print('Dockerfile successfully built.') + print('[onecc-docker] Build docker image ...') + _run(build_cmd, timeout=30) + print('[onecc-docker] Docker image is built successfully.') contianer_name = f"onecc_{recent_version.replace('.','_')}" user_cmd = ' '.join(onecc_arguments) diff --git a/compiler/oops/requires.cmake b/compiler/oops/requires.cmake new file mode 100644 index 0000000..f68ab16 --- /dev/null +++ b/compiler/oops/requires.cmake @@ -0,0 +1 @@ +require("pepper-str") diff --git a/compiler/pics/CMakeLists.txt b/compiler/pics/CMakeLists.txt index 80e6a1c..053d6a0 100644 --- a/compiler/pics/CMakeLists.txt +++ b/compiler/pics/CMakeLists.txt @@ -11,7 +11,7 @@ unset(PICS_DEPS) ### set(CIRCLE_SCHEMA_PYTHON_DIR "${CMAKE_CURRENT_BINARY_DIR}/circle") -get_target_property(SCHEMA_BIN_PATH mio_circle04 BINARY_DIR) +get_target_property(SCHEMA_BIN_PATH mio_circle06 BINARY_DIR) add_custom_command( OUTPUT ${CIRCLE_SCHEMA_PYTHON_DIR} diff --git a/compiler/pics/requires.cmake b/compiler/pics/requires.cmake new file mode 100644 index 0000000..1b77457 --- /dev/null +++ b/compiler/pics/requires.cmake @@ -0,0 +1 @@ +require("mio-circle06") diff --git a/compiler/pota-quantization-value-test/CMakeLists.txt b/compiler/pota-quantization-value-test/CMakeLists.txt index 53233cd..ec86fd9 100644 --- a/compiler/pota-quantization-value-test/CMakeLists.txt +++ b/compiler/pota-quantization-value-test/CMakeLists.txt @@ -6,6 +6,7 @@ unset(QUANTIZATION_VALUE_TEST) unset(QUANTIZATION_VALUE_TEST_WITH_PARAM) unset(QUANTIZATION_CONFIG_VALUE_TEST) unset(QUANTIZATION_CONFIG_VALUE_TEST_WITH_PARAM) +unset(QUANTIZATION_WO_VALUE_TEST_WITH_PARAM) macro(addTest NAME GRANULARITY DTYPE) list(APPEND QUANTIZATION_VALUE_TEST ${NAME}) @@ -17,6 +18,10 @@ macro(addQConfTest NAME GRANULARITY DTYPE) list(APPEND QUANTIZATION_CONFIG_VALUE_TEST_WITH_PARAM ${NAME} ${GRANULARITY} ${DTYPE}) endmacro(addQConfTest) +macro(addWeightsOnlyTest NAME GRANULARITY DTYPE) + list(APPEND QUANTIZATION_WO_VALUE_TEST_WITH_PARAM ${NAME} ${GRANULARITY} ${DTYPE}) +endmacro(addWeightsOnlyTest) + # Read "test.lst" include("test.lst") # Read "test.local.lst" if exists @@ -29,12 +34,7 @@ get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/gen_h5_explicit_inputs.py" "${CMAKE_CURRENT_BINARY_DIR}/gen_h5_explicit_inputs.py" COPYONLY) -# TODO Run both 2.8.0 and 2.10.1 test for jammy -if(ONE_UBUNTU_CODENAME_JAMMY) - set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_10_1") -else(ONE_UBUNTU_CODENAME_JAMMY) - set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_8_0") -endif(ONE_UBUNTU_CODENAME_JAMMY) +set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_12_1") ### ### Generate test.config @@ -114,6 +114,14 @@ add_test( ${QUANTIZATION_CONFIG_VALUE_TEST_WITH_PARAM} ) +add_test( + NAME pota_wo_quantization_test + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/test_wo_quantization.sh" + "${TEST_CONFIG}" + "${ARTIFACTS_BIN_PATH}" + ${QUANTIZATION_WO_VALUE_TEST_WITH_PARAM} +) + set_tests_properties(pota_record_minmax_test PROPERTIES DEPENDS pota_fake_wquant_test) set_tests_properties(pota_quantization_test PROPERTIES DEPENDS pota_record_minmax_test) set_tests_properties(pota_parallel_record_minmax_test PROPERTIES DEPENDS pota_record_minmax_test) diff --git a/compiler/pota-quantization-value-test/compare_tensors.py b/compiler/pota-quantization-value-test/compare_tensors.py index 20e92c6..fceeba5 100755 --- a/compiler/pota-quantization-value-test/compare_tensors.py +++ b/compiler/pota-quantization-value-test/compare_tensors.py @@ -19,7 +19,9 @@ parser.add_argument('--expect_dir', type=str, required=True) parser.add_argument('--mode', type=str, required=True) args = parser.parse_args() -supported_modes = ["fake_quantization", "record_minmax", "quantization"] +supported_modes = [ + "fake_quantization", "record_minmax", "quantization", "weights_only_quantization" +] model = args.input_h5 expect_dir = args.expect_dir @@ -111,6 +113,10 @@ with h5.File(model, "r") as input: compare_record_minmax(input[tensor_name], tensor_name, expect_dir) elif mode == "quantization": compare_quantization(input[tensor_name], tensor_name, expect_dir) + elif mode == "weights_only_quantization": + # Assume weights have name "ker" + if tensor_name == "ker": + compare_quantization(input[tensor_name], tensor_name, expect_dir) else: raise SystemExit("Unsupproted mode.") diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/wo_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/wo_quantization/ker.json new file mode 100644 index 0000000..94c794f --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/wo_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/int8/wo_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int8/wo_quantization/ker.json new file mode 100644 index 0000000..6df116e --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int8/wo_quantization/ker.json @@ -0,0 +1,61 @@ +{ + "weights": [ + [ + [ + [ + 16, + 32 + ], + [ + -48, + -64 + ] + ], + [ + [ + -79, + 95 + ], + [ + -111, + 127 + ] + ] + ], + [ + [ + [ + 64, + -32 + ], + [ + 48, + -16 + ] + ], + [ + [ + -127, + -95 + ], + [ + 111, + 79 + ] + ] + ] + ], + "scale": [ + 0.06299212574958801, + 0.06299212574958801 + ], + "zero_point": 0.0, + "min": [ + -8.0, + -8.0 + ], + "max": [ + 8.0, + 8.0 + ] +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/wo_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/wo_quantization/ker.json new file mode 100644 index 0000000..d465a7c --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/wo_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/int8/wo_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int8/wo_quantization/ker.json new file mode 100644 index 0000000..13c9293 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int8/wo_quantization/ker.json @@ -0,0 +1,53 @@ +{ + "weights": [ + [ + [ + [ + 10, + 18, + 25, + 32 + ], + [ + -88, + 91, + -93, + 95 + ] + ], + [ + [ + 49, + 54, + 59, + 64 + ], + [ + 127, + -127, + 127, + -127 + ] + ] + ] + ], + "scale": [ + 0.10236220806837082, + 0.11023622006177902, + 0.11811023950576782, + 0.12598425149917603 + ], + "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/requires.cmake b/compiler/pota-quantization-value-test/requires.cmake index e8acc81..e832dba 100644 --- a/compiler/pota-quantization-value-test/requires.cmake +++ b/compiler/pota-quantization-value-test/requires.cmake @@ -2,5 +2,4 @@ require("record-minmax") require("circle-quantizer") require("circle-tensordump") require("common-artifacts") -require("mio-circle04") require("pics") diff --git a/compiler/pota-quantization-value-test/test.lst b/compiler/pota-quantization-value-test/test.lst index e169de5..e510e6b 100644 --- a/compiler/pota-quantization-value-test/test.lst +++ b/compiler/pota-quantization-value-test/test.lst @@ -60,3 +60,8 @@ addQConfTest(Split_000 channel uint8) addQConfTest(Split_000 channel int16) addQConfTest(TransposeConv_001 channel int16) addQConfTest(TransposeConv_001 layer uint8) + +addWeightsOnlyTest(Conv2D_004 channel int8) +addWeightsOnlyTest(Conv2D_004 channel int16) +addWeightsOnlyTest(DepthwiseConv2D_002 channel int8) +addWeightsOnlyTest(DepthwiseConv2D_002 channel int16) diff --git a/compiler/pota-quantization-value-test/test_wo_quantization.sh b/compiler/pota-quantization-value-test/test_wo_quantization.sh new file mode 100755 index 0000000..18c85fe --- /dev/null +++ b/compiler/pota-quantization-value-test/test_wo_quantization.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# This script tests the basic behavior of weights-only quantization +# +# HOW TO USE +# +# ./test_quantization.sh ... +# test.config : set ${CIRCLE_QUANTIZER_PATH} and ${CIRCLE_TENSORDUMP_PATH}" +# work_dir : build directory of quantization-value-test (ex: build/compiler/quantization-value-test) + +SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPARE_SCRIPT_PATH="${SOURCE_PATH}/compare_tensors.py" +CONFIG_PATH="$1"; shift +BIN_PATH=$(dirname "${CONFIG_PATH}") +TEST_INPUT_PATH="${SOURCE_PATH}/test_inputs" +WORKDIR="$1"; shift + +source "${CONFIG_PATH}" + +echo "-- Found CIRCLE_QUANTIZER: ${CIRCLE_QUANTIZER_PATH}" +echo "-- Found CIRCLE_TENSORDUMP: ${CIRCLE_TENSORDUMP_PATH}" +echo "-- Found workdir: ${WORKDIR}" + +TESTED=() +PASSED=() +FAILED=() + +pushd "${WORKDIR}" +while [ "$1" != "" ]; do + MODELNAME=$1; shift + GRANULARITY=$1; shift + DTYPE=$1; shift + TESTCASE="${MODELNAME}.${GRANULARITY}.${DTYPE}" + + TESTED+=("${TESTCASE}") + + TESTCASE_FILE="${WORKDIR}/${TESTCASE}" + TEST_RESULT_FILE="${BIN_PATH}/${TESTCASE}" + + PASSED_TAG="${TEST_RESULT_FILE}.wo_quantization.passed" + rm -f "${PASSED_TAG}" + + cat > "${TEST_RESULT_FILE}_wo_quantization.log" <( + exec 2>&1 + set -ex + + # Run circle-quantizer with --quantize_weights + "${CIRCLE_QUANTIZER_PATH}" \ + --quantize_weights float32 "${DTYPE}" "${GRANULARITY}" \ + "${WORKDIR}/${MODELNAME}.circle" \ + "${TEST_RESULT_FILE}.wo_quantized.circle" + + # Dump scale, zp, weights values (circle-tensordump) + "${CIRCLE_TENSORDUMP_PATH}" \ + "${TEST_RESULT_FILE}.wo_quantized.circle" \ + --tensors_to_hdf5 "${TEST_RESULT_FILE}.wo_quantized.circle.h5" + + # Compare result + "${VIRTUALENV}/bin/python" "${COMPARE_SCRIPT_PATH}" \ + --input_h5 "${TEST_RESULT_FILE}.wo_quantized.circle.h5" \ + --expect_dir "${SOURCE_PATH}/expected_outputs/${MODELNAME}/${GRANULARITY}/${DTYPE}/wo_quantization" \ + --mode weights_only_quantization + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("$TESTCASE") + else + FAILED+=("$TESTCASE") + fi +done +popd + +if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then + echo "FAILED" + for TEST in "${FAILED[@]}" + do + echo "- ${TEST}" + done + exit 255 +fi + +echo "PASSED" +exit 0 diff --git a/compiler/record-minmax-conversion-test/CMakeLists.txt b/compiler/record-minmax-conversion-test/CMakeLists.txt index 63fe33d..35a7f99 100644 --- a/compiler/record-minmax-conversion-test/CMakeLists.txt +++ b/compiler/record-minmax-conversion-test/CMakeLists.txt @@ -41,17 +41,6 @@ add_test( COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh" "${TEST_CONFIG}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" ${RECORD_MINMAX_CONVERSION_TEST} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test( - NAME record_minmax_conversion_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh" - "${TEST_CONFIG}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - ${RECORD_MINMAX_CONVERSION_TEST} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/record-minmax-thread-safety-test/CMakeLists.txt b/compiler/record-minmax-thread-safety-test/CMakeLists.txt index ba0f082..9d25ac3 100644 --- a/compiler/record-minmax-thread-safety-test/CMakeLists.txt +++ b/compiler/record-minmax-thread-safety-test/CMakeLists.txt @@ -2,6 +2,12 @@ if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) +# Disable the test if record-minmax-for-thread-test does not exist +if (NOT TARGET record-minmax-for-thread-test) + message(STATUS "record-minmax-thread-safety-test is disabled as record-minmax-for-thread-test was not built.") + return() +endif(NOT TARGET record-minmax-for-thread-test) + # Build record-minmax-for-thread-test if target arch is 64bit # Thread sanitizer is only available on 64bit machine # (https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual#supported-platforms) @@ -57,17 +63,6 @@ add_test( COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh" "${TEST_CONFIG}" "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_8_0" + "${NNCC_OVERLAY_DIR}/venv_2_12_1" ${RECORD_MINMAX_THREAD_SAFETY_TEST} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test( - NAME record_minmax_thread_safety_210_test - COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh" - "${TEST_CONFIG}" - "${ARTIFACTS_BIN_PATH}" - "${NNCC_OVERLAY_DIR}/venv_2_10_1" - ${RECORD_MINMAX_THREAD_SAFETY_TEST} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/record-minmax/CMakeLists.txt b/compiler/record-minmax/CMakeLists.txt index 391e35b..3feca33 100644 --- a/compiler/record-minmax/CMakeLists.txt +++ b/compiler/record-minmax/CMakeLists.txt @@ -23,11 +23,17 @@ if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) +### +### record-minmax-for-thread-test is temporarily disabled, because +### gcc package has a bug. +### (https://bugs.launchpad.net/ubuntu/+source/gcc-10/+bug/2029910) +### Let's enable the target after the bug is fixed. +### # Build record-minmax-for-thread-test if target arch is 64bit # Thread sanitizer is only available on 64bit machine # (https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual#supported-platforms) -if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") - # create record-minmax-for-thread-test target +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" AND FALSE) +# create record-minmax-for-thread-test target # Note: record-minmax-for-thread-test is built with -fsanitize=thread so that thread sanitizer can check memory bugs, # record-minmax is built without the option for performance. add_executable(record-minmax-for-thread-test ${DRIVER} ${SOURCES}) @@ -46,11 +52,18 @@ if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") target_compile_options(record-minmax-for-thread-test PUBLIC -fsanitize=thread) target_link_libraries(record-minmax-for-thread-test -fsanitize=thread) -endif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") +endif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" AND FALSE) + +# record-minmax is executable, so we do not link it to the test. +# Instead, we use TEST_SOURCES to specify sources uesd for tests. +set(TEST_SOURCES + "src/RecordFunction.cpp" + "src/MinMaxComputer.cpp") file(GLOB_RECURSE TESTS "tests/*.test.cpp") nnas_find_package(GTest REQUIRED) -GTest_AddTest(record_minmax_function_test "${TESTS}") +GTest_AddTest(record_minmax_function_test ${TESTS} ${TEST_SOURCES}) target_include_directories(record_minmax_function_test PRIVATE include) +target_link_libraries(record_minmax_function_test luci_lang) target_link_libraries(record_minmax_function_test nncc_coverage) diff --git a/compiler/record-minmax/driver/Driver.cpp b/compiler/record-minmax/driver/Driver.cpp index d8ed95e..24a4ff8 100644 --- a/compiler/record-minmax/driver/Driver.cpp +++ b/compiler/record-minmax/driver/Driver.cpp @@ -63,6 +63,15 @@ int entry(const int argc, char **argv) .type(arser::DataType::FLOAT) .help("Record n'th percentile of max"); + arser.add_argument("--moving_avg_batch") + .type(arser::DataType::INT32) + .help("Batch size of moving average algorithm (default: 16)"); + + arser.add_argument("--moving_avg_const") + .type(arser::DataType::FLOAT) + .help("Hyperparameter (C) to compute moving average (default: 0.1). Update equation: avg <- " + "avg + C * (curr_batch_avg - avg)"); + arser.add_argument("--mode").help("Record mode. percentile (default) or moving_average"); arser.add_argument("--input_data_format") @@ -100,6 +109,8 @@ int entry(const int argc, char **argv) std::string mode("percentile"); float min_percentile = 1.0; float max_percentile = 99.0; + uint32_t moving_avg_batch = 16; + float moving_avg_const = 0.1; std::string input_data_format("h5"); uint32_t num_threads = 1; @@ -118,6 +129,12 @@ int entry(const int argc, char **argv) if (arser["--mode"]) mode = arser.get("--mode"); + if (arser["--moving_avg_batch"]) + moving_avg_batch = arser.get("--moving_avg_batch"); + + if (arser["--moving_avg_const"]) + moving_avg_const = arser.get("--moving_avg_const"); + if (mode != "percentile" && mode != "moving_average") throw std::runtime_error("Unsupported mode"); @@ -127,7 +144,23 @@ int entry(const int argc, char **argv) if (arser["--input_data_format"]) input_data_format = arser.get("--input_data_format"); - RecordMinMax rmm(num_threads); + std::unique_ptr computer; + { + if (mode == "percentile") + { + computer = make_percentile_computer(min_percentile, max_percentile); + } + else if (mode == "moving_average") + { + computer = make_moving_avg_computer(moving_avg_batch, moving_avg_const); + } + else + { + assert(false); + } + } + + RecordMinMax rmm(num_threads, std::move(computer)); // TODO: support parallel record for profile with random data if (num_threads > 1 and not arser["--input_data"]) @@ -152,11 +185,11 @@ int entry(const int argc, char **argv) { // Profile min/max while executing the H5 data if (num_threads == 1) - rmm.profileData(mode, input_data_path, min_percentile, max_percentile); + rmm.profileData(input_data_path); else { INFO(l) << "Using parallel recording" << std::endl; - rmm.profileDataInParallel(mode, input_data_path, min_percentile, max_percentile); + rmm.profileDataInParallel(input_data_path); } } // input_data is a text file having a file path in each line. @@ -170,13 +203,13 @@ int entry(const int argc, char **argv) else if (input_data_format == "list" || input_data_format == "filelist") { // Profile min/max while executing the list of Raw data - rmm.profileRawData(mode, input_data_path, min_percentile, max_percentile); + rmm.profileRawData(input_data_path); } else if (input_data_format == "directory" || input_data_format == "dir") { // Profile min/max while executing all files under the given directory // The contents of each file is same as the raw data in the 'list' type - rmm.profileRawDataDirectory(mode, input_data_path, min_percentile, max_percentile); + rmm.profileRawDataDirectory(input_data_path); } else { @@ -187,7 +220,7 @@ int entry(const int argc, char **argv) else { // Profile min/max while executing random input data - rmm.profileDataWithRandomInputs(mode, min_percentile, max_percentile); + rmm.profileDataWithRandomInputs(); } // Save profiled values to the model diff --git a/compiler/record-minmax/include/MinMaxComputer.h b/compiler/record-minmax/include/MinMaxComputer.h new file mode 100644 index 0000000..0e9c888 --- /dev/null +++ b/compiler/record-minmax/include/MinMaxComputer.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __RECORD_MINMAX_MINMAXCOMPUTER_H__ +#define __RECORD_MINMAX_MINMAXCOMPUTER_H__ + +#include "MinMaxVectors.h" + +#include + +#include +#include + +namespace record_minmax +{ + +class MinMaxComputer +{ +public: + MinMaxComputer() + { + // Do nothing + } + + virtual ~MinMaxComputer() = default; + + // Child class must implement this + virtual void + update_qparam(const std::unordered_map *minmax_map) = 0; +}; + +class PercentileComputer : public MinMaxComputer +{ +public: + PercentileComputer(float min_percentile, float max_percentile) + : _min_percentile(min_percentile), _max_percentile(max_percentile) + { + } + + virtual void + update_qparam(const std::unordered_map *minmax_map); + +private: + float _min_percentile = 0.0; + float _max_percentile = 0.0; +}; + +class MovingAvgComputer : public MinMaxComputer +{ +public: + MovingAvgComputer(uint32_t batch_size, float update_const) + : _batch_size(batch_size), _update_const(update_const) + { + } + + virtual void + update_qparam(const std::unordered_map *minmax_map); + +private: + uint32_t _batch_size = 0; + float _update_const = 0.0; +}; + +std::unique_ptr make_percentile_computer(float min_percentile, + float max_percentile); + +std::unique_ptr make_moving_avg_computer(uint32_t batch_size, + float moving_avg_const); + +} // namespace record_minmax + +#endif // __RECORD_MINMAX_MINMAXCOMPUTER_H__ diff --git a/compiler/record-minmax/include/MinMaxObserver.h b/compiler/record-minmax/include/MinMaxObserver.h index c34ac8e..f016797 100644 --- a/compiler/record-minmax/include/MinMaxObserver.h +++ b/compiler/record-minmax/include/MinMaxObserver.h @@ -20,18 +20,14 @@ #include #include +#include "MinMaxVectors.h" + #include #include namespace record_minmax { -struct MinMaxVectors -{ - std::vector min_vector; - std::vector max_vector; -}; - class MinMaxMap { public: @@ -72,6 +68,7 @@ public: void postTensorWrite(const luci::CircleNode *node, const luci_interpreter::Tensor *tensor) override; + // Never return nullptr const MinMaxMap *minMaxData() { return &_minmax_data; } private: diff --git a/compiler/record-minmax/include/MinMaxVectors.h b/compiler/record-minmax/include/MinMaxVectors.h new file mode 100644 index 0000000..5b2b03a --- /dev/null +++ b/compiler/record-minmax/include/MinMaxVectors.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __RECORD_MINMAX_MINMAXVECTORS_H__ +#define __RECORD_MINMAX_MINMAXVECTORS_H__ + +#include + +namespace record_minmax +{ + +struct MinMaxVectors +{ + std::vector min_vector; + std::vector max_vector; +}; + +} // namespace record_minmax + +#endif // __RECORD_MINMAX_MINMAXVECTORS_H__ diff --git a/compiler/record-minmax/include/RecordFunction.h b/compiler/record-minmax/include/RecordFunction.h index d5da01c..745ee22 100644 --- a/compiler/record-minmax/include/RecordFunction.h +++ b/compiler/record-minmax/include/RecordFunction.h @@ -14,12 +14,11 @@ * limitations under the License. */ +#ifndef __RECORD_MINMAX_RECORD_FUNCTION_H__ +#define __RECORD_MINMAX_RECORD_FUNCTION_H__ + #include -#include -#include -#include -#include -#include +#include namespace record_minmax { @@ -28,75 +27,15 @@ namespace record_minmax * @brief getNthPercentile calculates the n-th percentile of input vector (0.0 <= n <= 100.0) * linear interpolation is used when the desired percentile lies between two data points */ -float getNthPercentile(std::vector &vector, float percentile) -{ - if (percentile < 0 || percentile > 100) - throw std::runtime_error("Percentile must be ranged from 0 to 100"); - - if (vector.empty()) - throw std::runtime_error("Percentile must take a non-empty vector as an argument"); - - if (vector.size() == 1) - return vector[0]; - - std::vector copy; - copy.assign(vector.begin(), vector.end()); - std::sort(copy.begin(), copy.end()); - - if (percentile == 0.0) - return copy.front(); - - if (percentile == 100.0) - return copy.back(); - - int index = static_cast(std::floor((copy.size() - 1) * percentile / 100.0)); - - float percent_i = static_cast(index) / static_cast(copy.size() - 1); - float fraction = - (percentile / 100.0 - percent_i) / ((index + 1.0) / (copy.size() - 1.0) - percent_i); - float res = copy[index] + fraction * (copy[index + 1] - copy[index]); - return res; -} +float getNthPercentile(std::vector &vector, float percentile); /** * @brief getMovingAverage calculates the weighted moving average of input vector * The initial value is the minimum (or maximum) value of the first batch of the vector */ float getMovingAverage(const std::vector &vector, const float alpha, - const uint8_t batch_size, bool is_min) -{ - assert(!vector.empty()); - assert(alpha >= 0.0 && alpha <= 1.0); - assert(batch_size > 0); - - auto getBatchMinOrMax = [&](uint32_t start_index) { - assert(start_index < vector.size()); - - float res = is_min ? std::numeric_limits::max() : std::numeric_limits::lowest(); - for (uint32_t offset = 0; offset < batch_size; offset++) - { - uint32_t index = start_index + offset; - if (index >= vector.size()) - break; - - if (is_min) - { - res = vector[index] < res ? vector[index] : res; - } - else - { - res = vector[index] > res ? vector[index] : res; - } - } - return res; - }; - - float curr_avg = getBatchMinOrMax(0); - for (uint32_t i = batch_size; i < vector.size(); i += batch_size) - { - curr_avg = curr_avg * alpha + getBatchMinOrMax(i) * (1.0 - alpha); - } - return curr_avg; -} + const uint8_t batch_size, bool is_min); } // namespace record_minmax + +#endif // __RECORD_MINMAX_RECORD_FUNCTION_H__ diff --git a/compiler/record-minmax/include/RecordMinMax.h b/compiler/record-minmax/include/RecordMinMax.h index 2a7b88f..758e8a9 100644 --- a/compiler/record-minmax/include/RecordMinMax.h +++ b/compiler/record-minmax/include/RecordMinMax.h @@ -21,6 +21,7 @@ #include #include "MinMaxObserver.h" +#include "MinMaxComputer.h" #include #include @@ -35,35 +36,40 @@ using WholeOutput = std::vector; class RecordMinMax { public: - explicit RecordMinMax(uint32_t num_threads) : _threads_size(num_threads) + explicit RecordMinMax(uint32_t num_threads, std::unique_ptr &&minmax_computer) + : _threads_size(num_threads), _minmax_computer(std::move(minmax_computer)) { assert(_threads_size > 0); + assert(_minmax_computer != nullptr); } ~RecordMinMax() = default; void initialize(const std::string &input_model_path); - void profileData(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile); + // TODO Refactor profile functions + void profileData(const std::string &input_data_path); - void profileDataInParallel(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile); + void profileDataInParallel(const std::string &input_data_path); - void profileRawData(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile); + void profileRawData(const std::string &input_data_path); - void profileRawDataDirectory(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile); + void profileRawDataDirectory(const std::string &input_data_path); - void profileDataWithRandomInputs(const std::string &mode, float min_percentile, - float max_percentile); + void profileDataWithRandomInputs(void); void saveModel(const std::string &output_model_path); private: luci_interpreter::Interpreter *getInterpreter() const { return _interpreters[0].get(); } - MinMaxObserver *getObserver() const { return _observers[0].get(); } + + // Never return nullptr + MinMaxObserver *getObserver() const + { + assert(_observers.size() > 0); // FIX CALLER UNLESS + assert(_observers[0].get()); // FIX CALLER UNLESS + return _observers[0].get(); + } WholeOutput importH5Data(const std::string &input_data_path); @@ -74,6 +80,7 @@ private: std::vector> _observers; uint32_t _threads_size = 0; + std::unique_ptr _minmax_computer; }; } // namespace record_minmax diff --git a/compiler/record-minmax/src/MinMaxComputer.cpp b/compiler/record-minmax/src/MinMaxComputer.cpp new file mode 100644 index 0000000..5b8893a --- /dev/null +++ b/compiler/record-minmax/src/MinMaxComputer.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MinMaxComputer.h" +#include "RecordFunction.h" + +#include + +namespace record_minmax +{ + +void PercentileComputer::update_qparam( + const std::unordered_map *minmax_map) +{ + if (minmax_map == nullptr) + throw std::invalid_argument("minmax_map is nullptr"); + + for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + { + auto node = iter->first; + auto minmax = iter->second; + + auto min = getNthPercentile(minmax.min_vector, _min_percentile); + auto max = getNthPercentile(minmax.max_vector, _max_percentile); + + auto quantparam = std::make_unique(); + quantparam->min.push_back(min); + quantparam->max.push_back(max); + + assert(node->quantparam() == nullptr); + + auto mutable_node = const_cast(node); + mutable_node->quantparam(std::move(quantparam)); + } +} + +void MovingAvgComputer::update_qparam( + const std::unordered_map *minmax_map) +{ + if (minmax_map == nullptr) + throw std::invalid_argument("minmax_map is nullptr"); + + for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + { + auto node = iter->first; + auto minmax = iter->second; + + auto min = getMovingAverage(minmax.min_vector, 1 - _update_const, _batch_size, true); + auto max = getMovingAverage(minmax.max_vector, 1 - _update_const, _batch_size, false); + + auto quantparam = std::make_unique(); + quantparam->min.push_back(min); + quantparam->max.push_back(max); + + assert(node->quantparam() == nullptr); + + auto mutable_node = const_cast(node); + mutable_node->quantparam(std::move(quantparam)); + } +} + +std::unique_ptr make_percentile_computer(float min_percentile, float max_percentile) +{ + return std::make_unique(min_percentile, max_percentile); +} + +std::unique_ptr make_moving_avg_computer(uint32_t batch_size, + float moving_avg_const) +{ + return std::make_unique(batch_size, moving_avg_const); +} + +} // namespace record_minmax diff --git a/compiler/record-minmax/src/RecordFunction.cpp b/compiler/record-minmax/src/RecordFunction.cpp new file mode 100644 index 0000000..e812f82 --- /dev/null +++ b/compiler/record-minmax/src/RecordFunction.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RecordFunction.h" + +#include + +#include +#include +#include +#include +#include + +namespace record_minmax +{ + +float getNthPercentile(std::vector &vector, float percentile) +{ + if (percentile < 0 || percentile > 100) + throw std::runtime_error("Percentile must be ranged from 0 to 100"); + + if (vector.empty()) + throw std::runtime_error("Percentile must take a non-empty vector as an argument"); + + if (vector.size() == 1) + return vector[0]; + + std::vector copy; + copy.assign(vector.begin(), vector.end()); + std::sort(copy.begin(), copy.end()); + + if (percentile == 0.0) + return copy.front(); + + if (percentile == 100.0) + return copy.back(); + + int index = static_cast(std::floor((copy.size() - 1) * percentile / 100.0)); + + float percent_i = static_cast(index) / static_cast(copy.size() - 1); + float fraction = + (percentile / 100.0 - percent_i) / ((index + 1.0) / (copy.size() - 1.0) - percent_i); + float res = copy[index] + fraction * (copy[index + 1] - copy[index]); + return res; +} + +float getMovingAverage(const std::vector &vector, const float alpha, + const uint8_t batch_size, bool is_min) +{ + assert(!vector.empty()); + assert(alpha >= 0.0 && alpha <= 1.0); + assert(batch_size > 0); + + auto getBatchMinOrMax = [&](uint32_t start_index) { + assert(start_index < vector.size()); + + float res = is_min ? std::numeric_limits::max() : std::numeric_limits::lowest(); + for (uint32_t offset = 0; offset < batch_size; offset++) + { + uint32_t index = start_index + offset; + if (index >= vector.size()) + break; + + if (is_min) + { + res = vector[index] < res ? vector[index] : res; + } + else + { + res = vector[index] > res ? vector[index] : res; + } + } + return res; + }; + + float curr_avg = getBatchMinOrMax(0); + for (uint32_t i = batch_size; i < vector.size(); i += batch_size) + { + curr_avg = curr_avg * alpha + getBatchMinOrMax(i) * (1.0 - alpha); + } + return curr_avg; +} + +} // namespace record_minmax diff --git a/compiler/record-minmax/src/RecordMinMax.cpp b/compiler/record-minmax/src/RecordMinMax.cpp index c3da83a..d96b79c 100644 --- a/compiler/record-minmax/src/RecordMinMax.cpp +++ b/compiler/record-minmax/src/RecordMinMax.cpp @@ -15,7 +15,6 @@ */ #include "RecordMinMax.h" -#include "RecordFunction.h" #include "MinMaxObserver.h" #include @@ -144,38 +143,6 @@ void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, } } -void update_quantparam(record_minmax::MinMaxObserver *observer, const std::string &mode, - float min_percentile, float max_percentile) -{ - auto minmax_map = observer->minMaxData()->getMap(); - for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) - { - auto node = iter->first; - auto minmax = iter->second; - - float min{0.0f}, max{0.0f}; - if (mode == "percentile") - { - min = record_minmax::getNthPercentile(minmax.min_vector, min_percentile); - max = record_minmax::getNthPercentile(minmax.max_vector, max_percentile); - } - else if (mode == "moving_average") - { - min = record_minmax::getMovingAverage(minmax.min_vector, 0.9, 16, true); - max = record_minmax::getMovingAverage(minmax.max_vector, 0.9, 16, false); - } - assert(mode == "percentile" || mode == "moving_average"); - auto quantparam = std::make_unique(); - quantparam->min.push_back(min); - quantparam->max.push_back(max); - - assert(node->quantparam() == nullptr); - - auto mutable_node = const_cast(node); - mutable_node->quantparam(std::move(quantparam)); - } -} - } // namespace namespace record_minmax @@ -235,9 +202,7 @@ void RecordMinMax::initialize(const std::string &input_model_path) // The directory should contain binary files each of which is a raw data, // ready to be consumed by the input circle model without any modification // TODO reduce duplicate codes with profileRawData -void RecordMinMax::profileRawDataDirectory(const std::string &mode, - const std::string &input_data_path, float min_percentile, - float max_percentile) +void RecordMinMax::profileRawDataDirectory(const std::string &input_data_path) { struct dirent *entry = nullptr; DIR *dp = nullptr; @@ -297,7 +262,7 @@ void RecordMinMax::profileRawDataDirectory(const std::string &mode, std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - update_quantparam(getObserver(), mode, min_percentile, max_percentile); + _minmax_computer->update_qparam(getObserver()->minMaxData()->getMap()); } // input_data_path is a text file which specifies the representative data @@ -306,8 +271,7 @@ void RecordMinMax::profileRawDataDirectory(const std::string &mode, // ready to be consumed by the input circle model without any modification // NOTE If a model has multiple inputs, the binary file should have inputs concatenated in the same // order with the input index of the circle model. -void RecordMinMax::profileRawData(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile) +void RecordMinMax::profileRawData(const std::string &input_data_path) { std::ifstream input_file(input_data_path); if (input_file.fail()) @@ -357,7 +321,7 @@ void RecordMinMax::profileRawData(const std::string &mode, const std::string &in std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - update_quantparam(getObserver(), mode, min_percentile, max_percentile); + _minmax_computer->update_qparam(getObserver()->minMaxData()->getMap()); } WholeOutput RecordMinMax::importH5Data(const std::string &input_data_path) @@ -395,7 +359,7 @@ WholeOutput RecordMinMax::importH5Data(const std::string &input_data_path) { DataType dtype; Shape shape; - importer.readTensor(i, input_idx, &dtype, &shape, input_data.data()); + importer.readTensor(i, input_idx, &dtype, &shape, input_data.data(), input_data.size()); // Check the type and the shape of the input data is valid verifyTypeShape(input_node, dtype, shape); @@ -403,7 +367,7 @@ WholeOutput RecordMinMax::importH5Data(const std::string &input_data_path) else { // Skip type/shape check for raw data - importer.readTensor(i, input_idx, input_data.data()); + importer.readTensor(i, input_idx, input_data.data(), input_data.size()); } whole_output[i].emplace_back(std::move(input_data)); } @@ -418,8 +382,7 @@ WholeOutput RecordMinMax::importH5Data(const std::string &input_data_path) } } -void RecordMinMax::profileData(const std::string &mode, const std::string &input_data_path, - float min_percentile, float max_percentile) +void RecordMinMax::profileData(const std::string &input_data_path) { try { @@ -453,7 +416,8 @@ void RecordMinMax::profileData(const std::string &mode, const std::string &input { DataType dtype; Shape shape; - importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data()); + importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), + input_data.size()); // Check the type and the shape of the input data is valid verifyTypeShape(input_node, dtype, shape); @@ -461,7 +425,7 @@ void RecordMinMax::profileData(const std::string &mode, const std::string &input else { // Skip type/shape check for raw data - importer.readTensor(record_idx, input_idx, input_data.data()); + importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); } // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) @@ -480,12 +444,10 @@ void RecordMinMax::profileData(const std::string &mode, const std::string &input throw std::runtime_error("HDF5 error occurred."); } - update_quantparam(getObserver(), mode, min_percentile, max_percentile); + _minmax_computer->update_qparam(getObserver()->minMaxData()->getMap()); } -void RecordMinMax::profileDataInParallel(const std::string &mode, - const std::string &input_data_path, float min_percentile, - float max_percentile) +void RecordMinMax::profileDataInParallel(const std::string &input_data_path) { LOGGER(l); @@ -551,9 +513,8 @@ void RecordMinMax::profileDataInParallel(const std::string &mode, // End parallel part - // Copy all min, max values to one observer - auto observer = std::make_unique(); - auto main_min_max_map = const_cast(observer->minMaxData()); + // Copy all min, max values to one min/max map + MinMaxMap main_min_max_map; for (const auto &obs : _observers) { @@ -563,17 +524,16 @@ void RecordMinMax::profileDataInParallel(const std::string &mode, const auto node = iter.first; const auto &minmax = iter.second; - main_min_max_map->appendMinMaxVector(node, minmax); + main_min_max_map.appendMinMaxVector(node, minmax); } } std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - update_quantparam(observer.get(), mode, min_percentile, max_percentile); + _minmax_computer->update_qparam(main_min_max_map.getMap()); } -void RecordMinMax::profileDataWithRandomInputs(const std::string &mode, float min_percentile, - float max_percentile) +void RecordMinMax::profileDataWithRandomInputs(void) { // We use three randomly-generated records const uint32_t num_records = 3; @@ -641,7 +601,7 @@ void RecordMinMax::profileDataWithRandomInputs(const std::string &mode, float mi std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - update_quantparam(getObserver(), mode, min_percentile, max_percentile); + _minmax_computer->update_qparam(getObserver()->minMaxData()->getMap()); } void RecordMinMax::saveModel(const std::string &output_model_path) diff --git a/compiler/record-minmax/tests/MinMaxComputer.test.cpp b/compiler/record-minmax/tests/MinMaxComputer.test.cpp new file mode 100644 index 0000000..41babc2 --- /dev/null +++ b/compiler/record-minmax/tests/MinMaxComputer.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MinMaxComputer.h" + +#include + +#include + +#include + +using namespace record_minmax; + +TEST(MinMaxComputerTest, percentile) +{ + auto computer = make_percentile_computer(0.0, 100.0); + + luci::CircleAdd node; + MinMaxVectors minmax; + { + minmax.min_vector = {1.0, 2.0, 3.0}; + minmax.max_vector = {4.0, 5.0, 6.0}; + } + std::unordered_map min_max_map; + min_max_map.insert({&node, minmax}); + + computer->update_qparam(&min_max_map); + + EXPECT_TRUE(node.quantparam() != nullptr); +} + +TEST(MinMaxComputerTest, percentile_nullptr_NEG) +{ + auto computer = make_percentile_computer(0.0, 100.0); + + EXPECT_ANY_THROW(computer->update_qparam(nullptr)); +} + +TEST(MinMaxComputerTest, moving_avg) +{ + auto computer = make_moving_avg_computer(1, 0.99); + + luci::CircleAdd node; + MinMaxVectors minmax; + { + minmax.min_vector = {1.0, 2.0, 3.0}; + minmax.max_vector = {4.0, 5.0, 6.0}; + } + std::unordered_map min_max_map; + min_max_map.insert({&node, minmax}); + + computer->update_qparam(&min_max_map); + + EXPECT_TRUE(node.quantparam() != nullptr); +} + +TEST(MinMaxComputerTest, moving_avg_nullptr_NEG) +{ + auto computer = make_moving_avg_computer(1, 0.99); + + EXPECT_ANY_THROW(computer->update_qparam(nullptr)); +} diff --git a/compiler/souschef/include/souschef/Data/Gaussian.h b/compiler/souschef/include/souschef/Data/Gaussian.h index c9ac571..801bff8 100644 --- a/compiler/souschef/include/souschef/Data/Gaussian.h +++ b/compiler/souschef/include/souschef/Data/Gaussian.h @@ -105,6 +105,22 @@ private: float _stddev; }; +class GaussianInt8DataChef final : public DataChef +{ +public: + GaussianInt8DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + struct GaussianFloat32DataChefFactory : public DataChefFactory { std::unique_ptr create(const Arguments &args) const; @@ -130,6 +146,11 @@ struct GaussianFloat16DataChefFactory : public DataChefFactory std::unique_ptr create(const Arguments &args) const; }; +struct GaussianInt8DataChefFactory : public DataChefFactory +{ + std::unique_ptr create(const Arguments &args) const; +}; + } // namespace souschef #endif // __SOUSCHEF_DATA_GAUSSIAN_H__ diff --git a/compiler/souschef/src/Gaussian.cpp b/compiler/souschef/src/Gaussian.cpp index a180e24..71ab567 100644 --- a/compiler/souschef/src/Gaussian.cpp +++ b/compiler/souschef/src/Gaussian.cpp @@ -115,6 +115,11 @@ std::vector GaussianUint8DataChef::generate(int32_t count) const return generate_gaussian(count, _mean, _stddev); } +std::vector GaussianInt8DataChef::generate(int32_t count) const +{ + return generate_gaussian(count, _mean, _stddev); +} + std::unique_ptr GaussianFloat32DataChefFactory::create(const Arguments &args) const { if (args.count() != 2) @@ -167,6 +172,19 @@ std::unique_ptr GaussianUint8DataChefFactory::create(const Arguments & return std::unique_ptr{new GaussianUint8DataChef{mean, stddev}}; } +std::unique_ptr GaussianInt8DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number(args.value(0)); + auto const stddev = to_number(args.value(1)); + + return std::unique_ptr{new GaussianInt8DataChef{mean, stddev}}; +} + std::unique_ptr GaussianFloat16DataChefFactory::create(const Arguments &args) const { if (args.count() != 2) diff --git a/compiler/souschef/src/LexicalCast.cpp b/compiler/souschef/src/LexicalCast.cpp index 8ab8770..4468f1e 100644 --- a/compiler/souschef/src/LexicalCast.cpp +++ b/compiler/souschef/src/LexicalCast.cpp @@ -45,6 +45,13 @@ template <> uint8_t to_number(const std::string &s) assert(temp <= std::numeric_limits::max()); return static_cast(temp); } +template <> int8_t to_number(const std::string &s) +{ + int temp = std::stoi(s); + assert(temp >= std::numeric_limits::min()); + assert(temp <= std::numeric_limits::max()); + return static_cast(temp); +} template <> bool to_number(const std::string &s) { if (s == "T" || s == "t" || s == "TRUE" || s == "true" || s == "1") diff --git a/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt index ded4162..017fd6a 100644 --- a/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt +++ b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt @@ -76,12 +76,7 @@ list(APPEND TEST_DEPS "${TEST_RUNNER}") get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) -# TODO Run both 2.8.0 and 2.10.1 test for jammy -if(ONE_UBUNTU_CODENAME_JAMMY) - set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_10_1") -else() - set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_8_0") -endif() +set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_2_12_1") ### ### Generate test.config diff --git a/compiler/tfl-inspect/CMakeLists.txt b/compiler/tfl-inspect/CMakeLists.txt index 2c6e3a1..c91ec96 100644 --- a/compiler/tfl-inspect/CMakeLists.txt +++ b/compiler/tfl-inspect/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_tflite280) +if(NOT TARGET mio_tflite2121) return() -endif(NOT TARGET mio_tflite280) +endif(NOT TARGET mio_tflite2121) set(DRIVER "driver/Driver.cpp") @@ -10,6 +10,6 @@ add_executable(tfl-inspect ${DRIVER} ${SOURCES}) target_include_directories(tfl-inspect PRIVATE src) target_link_libraries(tfl-inspect arser) target_link_libraries(tfl-inspect foder) -target_link_libraries(tfl-inspect mio_tflite280) -target_link_libraries(tfl-inspect mio_tflite280_helper) +target_link_libraries(tfl-inspect mio_tflite2121) +target_link_libraries(tfl-inspect mio_tflite2121_helper) target_link_libraries(tfl-inspect safemain) diff --git a/compiler/tfl-inspect/requires.cmake b/compiler/tfl-inspect/requires.cmake index a11f6b2..80dc592 100644 --- a/compiler/tfl-inspect/requires.cmake +++ b/compiler/tfl-inspect/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-tflite280") +require("mio-tflite2121") require("safemain") diff --git a/compiler/tfl-inspect/src/Reader.cpp b/compiler/tfl-inspect/src/Reader.cpp index 6c45295..1ae2e47 100644 --- a/compiler/tfl-inspect/src/Reader.cpp +++ b/compiler/tfl-inspect/src/Reader.cpp @@ -16,7 +16,7 @@ #include "Reader.h" -#include +#include #include #include diff --git a/compiler/tfl-verify/CMakeLists.txt b/compiler/tfl-verify/CMakeLists.txt index 5bead5b..4e03722 100644 --- a/compiler/tfl-verify/CMakeLists.txt +++ b/compiler/tfl-verify/CMakeLists.txt @@ -1,6 +1,6 @@ -if(NOT TARGET mio_tflite280) +if(NOT TARGET mio_tflite2121) return() -endif(NOT TARGET mio_tflite280) +endif(NOT TARGET mio_tflite2121) file(GLOB_RECURSE SOURCES "src/*.cpp") @@ -8,6 +8,6 @@ add_executable(tfl-verify ${SOURCES}) target_include_directories(tfl-verify PRIVATE src) target_link_libraries(tfl-verify arser) target_link_libraries(tfl-verify foder) -target_link_libraries(tfl-verify mio_tflite280) +target_link_libraries(tfl-verify mio_tflite2121) target_link_libraries(tfl-verify safemain) target_link_libraries(tfl-verify cwrap) diff --git a/compiler/tfl-verify/requires.cmake b/compiler/tfl-verify/requires.cmake index b107bdf..9579c27 100644 --- a/compiler/tfl-verify/requires.cmake +++ b/compiler/tfl-verify/requires.cmake @@ -1,5 +1,5 @@ require("arser") require("foder") -require("mio-tflite280") +require("mio-tflite2121") require("safemain") require("cwrap") diff --git a/compiler/tflchef/CMakeLists.txt b/compiler/tflchef/CMakeLists.txt index 6205ac6..73a8a0d 100644 --- a/compiler/tflchef/CMakeLists.txt +++ b/compiler/tflchef/CMakeLists.txt @@ -5,10 +5,10 @@ if(NOT Protobuf_FOUND) return() endif(NOT Protobuf_FOUND) -if(NOT TARGET mio_tflite280) - message(STATUS "Build tflchef: FAILED (missing mio_tflite280)") +if(NOT TARGET mio_tflite2121) + message(STATUS "Build tflchef: FAILED (missing mio_tflite2121)") return() -endif(NOT TARGET mio_tflite280) +endif(NOT TARGET mio_tflite2121) # Recipe Parser add_subdirectory(proto) diff --git a/compiler/tflchef/core/CMakeLists.txt b/compiler/tflchef/core/CMakeLists.txt index 6b6fed5..b9f7352 100644 --- a/compiler/tflchef/core/CMakeLists.txt +++ b/compiler/tflchef/core/CMakeLists.txt @@ -5,5 +5,5 @@ target_include_directories(tflchef_core PUBLIC include) target_include_directories(tflchef_core PRIVATE src) target_link_libraries(tflchef_core tflchef_proto) target_link_libraries(tflchef_core tflchef_log) -target_link_libraries(tflchef_core mio_tflite280) +target_link_libraries(tflchef_core mio_tflite2121) target_link_libraries(tflchef_core souschef) diff --git a/compiler/tflchef/core/src/Convert.cpp b/compiler/tflchef/core/src/Convert.cpp index f4dd4b3..d1babf0 100644 --- a/compiler/tflchef/core/src/Convert.cpp +++ b/compiler/tflchef/core/src/Convert.cpp @@ -77,6 +77,8 @@ tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value) return tflite::TensorType_BOOL; case tflchef::INT16: return tflite::TensorType_INT16; + case tflchef::INT8: + return tflite::TensorType_INT8; default: break; } diff --git a/compiler/tflchef/core/src/CustomOp/Erf.cpp b/compiler/tflchef/core/src/CustomOp/Erf.cpp new file mode 100644 index 0000000..f611b68 --- /dev/null +++ b/compiler/tflchef/core/src/CustomOp/Erf.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2015 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 "Erf.h" + +#include + +flatbuffers::Offset ErfChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + return flatbuffers::Offset(); +} + +flatbuffers::Offset> +ErfChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.type() == "Erf"); + + /** + * REGISTER_OP("Erf") + .Input("x: T") + .Output("y: T") + .Attr("T: {bfloat16, half, float, double}") + .SetShapeFn(shape_inference::UnchangedShape) + */ + + auto flex_buffers = std::make_unique(); + size_t map_start = flex_buffers->StartMap(); + + // TODO Support more data types + flex_buffers->Int("T", tflite::TensorType_FLOAT32); + + flex_buffers->EndMap(map_start); + flex_buffers->Finish(); + + auto circle_custom_options = fbb.CreateVector(flex_buffers->GetBuffer()); + return circle_custom_options; +} + +std::unique_ptr ErfChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr{new ErfChef{operation}}; +} diff --git a/compiler/tflchef/core/src/CustomOp/Erf.h b/compiler/tflchef/core/src/CustomOp/Erf.h new file mode 100644 index 0000000..192c5f3 --- /dev/null +++ b/compiler/tflchef/core/src/CustomOp/Erf.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_ERF_H__ +#define __OP_ERF_H__ + +#include "OpChef.h" + +class ErfChef final : public OpChef +{ +public: + explicit ErfChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_CUSTOM; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; } + + flatbuffers::Offset value(flatbuffers::FlatBufferBuilder &fbb) const override; + + flatbuffers::Offset> + custom_value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct ErfChefFactory final : public OpChefFactory +{ + std::unique_ptr create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_ERF_H__ diff --git a/compiler/tflchef/core/src/DataChef.def b/compiler/tflchef/core/src/DataChef.def index 28a5b76..abe6426 100644 --- a/compiler/tflchef/core/src/DataChef.def +++ b/compiler/tflchef/core/src/DataChef.def @@ -7,12 +7,14 @@ DATA_CHEF(FLOAT32, constant, ConstantDataChefFactory) DATA_CHEF(BOOL, constant, ConstantDataChefFactory) DATA_CHEF(UINT8, constant, ConstantDataChefFactory) +DATA_CHEF(INT8, constant, ConstantDataChefFactory) DATA_CHEF(INT16, constant, ConstantDataChefFactory) DATA_CHEF(INT32, constant, ConstantDataChefFactory) DATA_CHEF(INT64, constant, ConstantDataChefFactory) DATA_CHEF(INT64, explicit, ExplicitDataChefFactory) DATA_CHEF(INT32, explicit, ExplicitDataChefFactory) DATA_CHEF(INT16, explicit, ExplicitDataChefFactory) +DATA_CHEF(INT8, explicit, ExplicitDataChefFactory) DATA_CHEF(UINT8, explicit, ExplicitDataChefFactory) DATA_CHEF(BOOL, explicit, ExplicitDataChefFactory) DATA_CHEF(FLOAT32, explicit, ExplicitDataChefFactory) @@ -20,6 +22,7 @@ DATA_CHEF(STRING, explicit, ExplicitDataChefFactory) DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory) DATA_CHEF(INT32, gaussian, GaussianInt32DataChefFactory) DATA_CHEF(INT16, gaussian, GaussianInt16DataChefFactory) +DATA_CHEF(INT8, gaussian, GaussianInt8DataChefFactory) DATA_CHEF(UINT8, gaussian, GaussianUint8DataChefFactory) // FLOAT16 support for only gaussian, explicit for now diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp index a788adc..3afcd23 100644 --- a/compiler/tflchef/core/src/ModelChef.cpp +++ b/compiler/tflchef/core/src/ModelChef.cpp @@ -93,6 +93,7 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) static DataChefRegistry boolean; static DataChefRegistry s16; static DataChefRegistry fp16; + static DataChefRegistry s8; switch (type) { @@ -112,6 +113,8 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) return boolean; case tflchef::INT16: return s16; + case tflchef::INT8: + return s8; default: break; } @@ -734,9 +737,19 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) for (auto const &opcode : builtin_code_map) { tflite::OperatorCodeBuilder code_builder{*flatbuffer_builder}; - // TODO support for opcode.first >= 127 - assert(opcode.first < 127); - code_builder.add_deprecated_builtin_code(opcode.first); + // 127 is BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES + // This is the way to handle deprecated builtin code + // See + // https://github.com/tensorflow/tensorflow/blob/a0afe8f9218be5eb9ed5dffc2dff652996da8c28/tensorflow/lite/schema/schema.fbs#L1061-L1077 + if (opcode.first < 127) + { + code_builder.add_deprecated_builtin_code(opcode.first); + } + else + { + code_builder.add_deprecated_builtin_code( + ::tflite::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES); + } code_builder.add_version(opcode.second); code_builder.add_builtin_code(opcode.first); auto code = code_builder.Finish(); diff --git a/compiler/tflchef/core/src/Op/Gelu.cpp b/compiler/tflchef/core/src/Op/Gelu.cpp new file mode 100644 index 0000000..91d2bb3 --- /dev/null +++ b/compiler/tflchef/core/src/Op/Gelu.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gelu.h" + +flatbuffers::Offset GeluChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + assert(_operation->has_gelu_options()); + + const auto &options = _operation->gelu_options(); + + tflite::GeluOptionsBuilder options_builder{fbb}; + options_builder.add_approximate(options.approximate()); + + return options_builder.Finish().Union(); +} + +std::unique_ptr GeluChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr{new GeluChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/Gelu.h b/compiler/tflchef/core/src/Op/Gelu.h new file mode 100644 index 0000000..64d9361 --- /dev/null +++ b/compiler/tflchef/core/src/Op/Gelu.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GELU_H__ +#define __OP_GELU_H__ + +#include "OpChef.h" + +class GeluChef final : public OpChef +{ +public: + explicit GeluChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_GELU; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_GeluOptions; } + + flatbuffers::Offset value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct GeluChefFactory final : public OpChefFactory +{ + std::unique_ptr create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_GELU_H__ diff --git a/compiler/tflchef/core/src/Op/HardSwish.cpp b/compiler/tflchef/core/src/Op/HardSwish.cpp new file mode 100644 index 0000000..27ab8b5 --- /dev/null +++ b/compiler/tflchef/core/src/Op/HardSwish.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "HardSwish.h" + +flatbuffers::Offset HardSwishChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + return flatbuffers::Offset(); +} + +std::unique_ptr HardSwishChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr{new HardSwishChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/HardSwish.h b/compiler/tflchef/core/src/Op/HardSwish.h new file mode 100644 index 0000000..10ed51e --- /dev/null +++ b/compiler/tflchef/core/src/Op/HardSwish.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_HARDSWISH_H__ +#define __OP_HARDSWISH_H__ + +#include "OpChef.h" + +class HardSwishChef final : public OpChef +{ +public: + explicit HardSwishChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_HARD_SWISH; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; } + + flatbuffers::Offset value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct HardSwishChefFactory final : public OpChefFactory +{ + std::unique_ptr create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_HARDSWISH_H__ diff --git a/compiler/tflchef/core/src/Op/TransposeConv.cpp b/compiler/tflchef/core/src/Op/TransposeConv.cpp index c9e4527..530ebae 100644 --- a/compiler/tflchef/core/src/Op/TransposeConv.cpp +++ b/compiler/tflchef/core/src/Op/TransposeConv.cpp @@ -34,6 +34,13 @@ flatbuffers::Offset TransposeConvChef::value(flatbuffers::FlatBufferBuilde options_builder.add_stride_h(operation.transpose_conv_options().stride_h()); options_builder.add_stride_w(operation.transpose_conv_options().stride_w()); + // TODO remove calling has_activation + auto chef_activation = operation.transpose_conv_options().has_activation() + ? operation.transpose_conv_options().activation() + : tflchef::NONE; + auto tflite_activation = as_tflite_activation(chef_activation); + options_builder.add_fused_activation_function(tflite_activation); + return options_builder.Finish().Union(); } diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def index c19d00d..9a21646 100644 --- a/compiler/tflchef/core/src/OpChef.def +++ b/compiler/tflchef/core/src/OpChef.def @@ -35,8 +35,10 @@ OP_CHEF(FloorMod, FloorModChefFactory) OP_CHEF(FullyConnected, FullyConnectedChefFactory) OP_CHEF(Gather, GatherChefFactory) OP_CHEF(GatherNd, GatherNdChefFactory) +OP_CHEF(Gelu, GeluChefFactory) OP_CHEF(Greater, GreaterChefFactory) OP_CHEF(GreaterEqual, GreaterEqualChefFactory) +OP_CHEF(HardSwish, HardSwishChefFactory) OP_CHEF(If, IfChefFactory) OP_CHEF(L2Normalize, L2NormalizeChefFactory) OP_CHEF(L2Pool2D, L2Pool2DChefFactory) @@ -123,6 +125,7 @@ OP_CHEF(AddV2, AddV2ChefFactory) OP_CHEF(All, AllChefFactory) OP_CHEF(BatchMatMulV2, BatchMatMulV2ChefFactory) OP_CHEF(BroadcastTo, BroadcastToChefFactory) +OP_CHEF(Erf, ErfChefFactory) OP_CHEF(MatMul, MatMulChefFactory) OP_CHEF(MatrixBandPart, MatrixBandPartChefFactory) OP_CHEF(MaxPoolWithArgmax, MaxPoolWithArgmaxChefFactory) diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h index 3cd3be5..ba2b175 100644 --- a/compiler/tflchef/core/src/OpChefs.h +++ b/compiler/tflchef/core/src/OpChefs.h @@ -48,8 +48,10 @@ #include "Op/FullyConnected.h" #include "Op/Gather.h" #include "Op/GatherNd.h" +#include "Op/Gelu.h" #include "Op/Greater.h" #include "Op/GreaterEqual.h" +#include "Op/HardSwish.h" #include "Op/If.h" #include "Op/L2Normalize.h" #include "Op/L2Pool2D.h" @@ -135,6 +137,7 @@ #include "CustomOp/All.h" #include "CustomOp/BatchMatMulV2.h" #include "CustomOp/BroadcastTo.h" +#include "CustomOp/Erf.h" #include "CustomOp/MatMul.h" #include "CustomOp/MatrixBandPart.h" #include "CustomOp/MaxPoolWithArgmax.h" diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto index da4b692..98ae2b2 100644 --- a/compiler/tflchef/proto/tflchef.proto +++ b/compiler/tflchef/proto/tflchef.proto @@ -22,6 +22,7 @@ enum TensorType { STRING = 5; BOOL = 6; INT16 = 7; + INT8 = 9; } enum DimensionType { @@ -436,6 +437,10 @@ message GatherNdOptions { // None } +message GeluOptions { + optional bool approximate = 1 [default = false]; +} + message NonMaxSuppressionV4Options { // None } @@ -473,6 +478,7 @@ message TransposeConvOptions { optional Padding padding = 1 [default = VALID]; optional int32 stride_w = 2 [default = 1]; optional int32 stride_h = 3 [default = 1]; + optional Activation activation = 4 [default = NONE]; } message ReverseSequenceOptions { @@ -662,6 +668,7 @@ message Operation { optional MatMulOptions matmul_options = 208; optional MaxPoolWithArgmaxOptions max_pool_with_argmax_options = 209; optional DensifyOptions densify_options = 210; + optional GeluOptions gelu_options = 211; // NOTE if there are more than two options with same type of Options // use the number not listed in the above reserve list } diff --git a/compiler/tflchef/requires.cmake b/compiler/tflchef/requires.cmake index a01da42..8d86632 100644 --- a/compiler/tflchef/requires.cmake +++ b/compiler/tflchef/requires.cmake @@ -1,7 +1,7 @@ require("arser") require("nnkit") require("cwrap") -require("mio-tflite280") +require("mio-tflite2121") require("safemain") require("hermes") require("hermes-std") diff --git a/compiler/tflchef/tests/CMakeLists.txt b/compiler/tflchef/tests/CMakeLists.txt index 26cf67f..79b0084 100644 --- a/compiler/tflchef/tests/CMakeLists.txt +++ b/compiler/tflchef/tests/CMakeLists.txt @@ -124,9 +124,9 @@ add_custom_target(tflchef_testfiles ALL DEPENDS ${TESTFILES}) # Using mio_tflite_validate for temporary as it only calls flatbuffer validate # TODO do testing with running the model with runtime/interpreter -# NOTE for ARM32 cross build, $ is used as-is +# NOTE for ARM32 cross build, $ is used as-is # as test should run in ARM32 device add_test(NAME tflchef_test COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runvalidate.sh" - $ + $ ${TESTS}) diff --git a/compiler/tflchef/tests/custom_erf/test.recipe b/compiler/tflchef/tests/custom_erf/test.recipe new file mode 100644 index 0000000..ab093a3 --- /dev/null +++ b/compiler/tflchef/tests/custom_erf/test.recipe @@ -0,0 +1,17 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operation { + type: "Erf" + input: "ifm" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/compiler/tflchef/tflite/CMakeLists.txt b/compiler/tflchef/tflite/CMakeLists.txt index d9a20a2..bf20f31 100644 --- a/compiler/tflchef/tflite/CMakeLists.txt +++ b/compiler/tflchef/tflite/CMakeLists.txt @@ -5,7 +5,7 @@ target_include_directories(tflchef_tflite PUBLIC include) target_include_directories(tflchef_tflite PRIVATE src) target_include_directories(tflchef_tflite PRIVATE src/Op/include) target_link_libraries(tflchef_tflite tflchef_proto) -target_link_libraries(tflchef_tflite mio_tflite280) -target_link_libraries(tflchef_tflite mio_tflite280_helper) +target_link_libraries(tflchef_tflite mio_tflite2121) +target_link_libraries(tflchef_tflite mio_tflite2121_helper) target_link_libraries(tflchef_tflite cwrap) target_link_libraries(tflchef_tflite souschef) diff --git a/compiler/tflchef/tflite/src/Convert.cpp b/compiler/tflchef/tflite/src/Convert.cpp index 2429876..46812f4 100644 --- a/compiler/tflchef/tflite/src/Convert.cpp +++ b/compiler/tflchef/tflite/src/Convert.cpp @@ -33,6 +33,8 @@ tflchef::TensorType as_tflchef_type(const tflite::TensorType type) return tflchef::UINT8; case tflite::TensorType_BOOL: return tflchef::BOOL; + case tflite::TensorType_INT8: + return tflchef::INT8; case tflite::TensorType_INT16: return tflchef::INT16; case tflite::TensorType_FLOAT16: diff --git a/compiler/tflchef/tflite/src/Op/Gelu.cpp b/compiler/tflchef/tflite/src/Op/Gelu.cpp new file mode 100644 index 0000000..23cee07 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/Gelu.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gelu.h" + +#include "Convert.h" + +namespace tflchef +{ + +void TFliteOpGelu::filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + // Nothing to do with filler +} + +tflchef::Operation *TFliteOpGelu::build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_GeluOptions(); + + auto operation = model_recipe->add_operation(); + + operation->set_type("Gelu"); + + auto *op_options = operation->mutable_gelu_options(); + + op_options->set_approximate(op_params->approximate()); + + return operation; +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/Op/HardSwish.cpp b/compiler/tflchef/tflite/src/Op/HardSwish.cpp new file mode 100644 index 0000000..2282ff9 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/HardSwish.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "HardSwish.h" + +#include "Convert.h" + +namespace tflchef +{ + +void TFliteOpHardSwish::filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + // Nothing to do with filler +} + +tflchef::Operation *TFliteOpHardSwish::build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + auto operation = model_recipe->add_operation(); + + operation->set_type("HardSwish"); + + return operation; +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/Op/PRelu.cpp b/compiler/tflchef/tflite/src/Op/PRelu.cpp index 8a5e83a..1a1a84b 100644 --- a/compiler/tflchef/tflite/src/Op/PRelu.cpp +++ b/compiler/tflchef/tflite/src/Op/PRelu.cpp @@ -24,6 +24,11 @@ namespace tflchef void TFliteOpPRelu::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { + const std::vector &inputs = as_index_vector(op->inputs()); + + assert(inputs.size() == 2); + + import->set_tensor_filler(inputs.at(1)); // alpha } tflchef::Operation *TFliteOpPRelu::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/TransposeConv.cpp b/compiler/tflchef/tflite/src/Op/TransposeConv.cpp index 4e7adf6..875ccb5 100644 --- a/compiler/tflchef/tflite/src/Op/TransposeConv.cpp +++ b/compiler/tflchef/tflite/src/Op/TransposeConv.cpp @@ -53,10 +53,12 @@ tflchef::Operation *TFliteOpTransposeConv::build(const tflite::Operator *op, TFl operation->set_type("TransposeConv"); auto op_options = operation->mutable_transpose_conv_options(); + auto tflchef_activation = as_tflchef_activation(op_params->fused_activation_function()); op_options->set_stride_h(op_params->stride_h()); op_options->set_stride_w(op_params->stride_w()); op_options->set_padding(as_tflchef_padding(op_params->padding())); + op_options->set_activation(tflchef_activation); return operation; } diff --git a/compiler/tflchef/tflite/src/Op/include/Gelu.h b/compiler/tflchef/tflite/src/Op/include/Gelu.h new file mode 100644 index 0000000..0c51a51 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/include/Gelu.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GELU_H__ +#define __TFLITE_OP_GELU_H__ + +#include "TFliteOpChef.h" + +namespace tflchef +{ + +/** + * @brief tflchef operator builder for Gelu + */ +class TFliteOpGelu : 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_GELU_H__ diff --git a/compiler/tflchef/tflite/src/Op/include/HardSwish.h b/compiler/tflchef/tflite/src/Op/include/HardSwish.h new file mode 100644 index 0000000..d9b5a53 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/include/HardSwish.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_HARDSWISH_H__ +#define __TFLITE_OP_HARDSWISH_H__ + +#include "TFliteOpChef.h" + +namespace tflchef +{ + +/** + * @brief tflchef operator builder for Hard Swish + */ +class TFliteOpHardSwish : 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_HARDSWISH_H__ diff --git a/compiler/tflchef/tflite/src/RecipeChef.cpp b/compiler/tflchef/tflite/src/RecipeChef.cpp index 0701707..2203f59 100644 --- a/compiler/tflchef/tflite/src/RecipeChef.cpp +++ b/compiler/tflchef/tflite/src/RecipeChef.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include "Convert.h" #include "TFliteImport.h" diff --git a/compiler/tflchef/tflite/src/TFliteImport.cpp b/compiler/tflchef/tflite/src/TFliteImport.cpp index 7114ab0..9abec9a 100644 --- a/compiler/tflchef/tflite/src/TFliteImport.cpp +++ b/compiler/tflchef/tflite/src/TFliteImport.cpp @@ -18,7 +18,7 @@ #include "Convert.h" -#include +#include #include diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h index 1b9d420..34cb1ba 100644 --- a/compiler/tflchef/tflite/src/TFliteOpChefs.h +++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h @@ -48,8 +48,10 @@ #include "Op/include/FullyConnected.h" #include "Op/include/Gather.h" #include "Op/include/GatherNd.h" +#include "Op/include/Gelu.h" #include "Op/include/Greater.h" #include "Op/include/GreaterEqual.h" +#include "Op/include/HardSwish.h" #include "Op/include/L2Normalize.h" #include "Op/include/L2Pool2D.h" #include "Op/include/LeakyRelu.h" diff --git a/compiler/tflchef/tflite/src/TFliteOpRegistry.h b/compiler/tflchef/tflite/src/TFliteOpRegistry.h index 4cbe7cf..a37f15c 100644 --- a/compiler/tflchef/tflite/src/TFliteOpRegistry.h +++ b/compiler/tflchef/tflite/src/TFliteOpRegistry.h @@ -85,8 +85,10 @@ private: REG_TFL_OP(FULLY_CONNECTED, TFliteOpFullyConnected); REG_TFL_OP(GATHER, TFliteOpGather); REG_TFL_OP(GATHER_ND, TFliteOpGatherNd); + REG_TFL_OP(GELU, TFliteOpGelu); REG_TFL_OP(GREATER, TFliteOpGreater); REG_TFL_OP(GREATER_EQUAL, TFliteOpGreaterEqual); + REG_TFL_OP(HARD_SWISH, TFliteOpHardSwish); REG_TFL_OP(L2_NORMALIZATION, TFliteOpL2Normalize); REG_TFL_OP(L2_POOL_2D, TFliteOpL2Pool2D); REG_TFL_OP(LEAKY_RELU, TFliteOpLeakyRelu); diff --git a/compiler/tfldump/CMakeLists.txt b/compiler/tfldump/CMakeLists.txt index 4102326..3fe1ea9 100644 --- a/compiler/tfldump/CMakeLists.txt +++ b/compiler/tfldump/CMakeLists.txt @@ -1,7 +1,7 @@ -if(NOT TARGET mio_tflite280) - message(STATUS "Build tfldump: FAILED (missing mio_tflite280)") +if(NOT TARGET mio_tflite2121) + message(STATUS "Build tfldump: FAILED (missing mio_tflite2121)") return() -endif(NOT TARGET mio_tflite280) +endif(NOT TARGET mio_tflite2121) set(DRIVER "driver/Driver.cpp") @@ -11,6 +11,6 @@ add_executable(tfldump ${DRIVER} ${SOURCES}) target_include_directories(tfldump PRIVATE include) target_link_libraries(tfldump arser) target_link_libraries(tfldump foder) -target_link_libraries(tfldump mio_tflite280) -target_link_libraries(tfldump mio_tflite280_helper) +target_link_libraries(tfldump mio_tflite2121) +target_link_libraries(tfldump mio_tflite2121_helper) target_link_libraries(tfldump safemain) diff --git a/compiler/tfldump/requires.cmake b/compiler/tfldump/requires.cmake index a11f6b2..80dc592 100644 --- a/compiler/tfldump/requires.cmake +++ b/compiler/tfldump/requires.cmake @@ -1,4 +1,4 @@ require("arser") require("foder") -require("mio-tflite280") +require("mio-tflite2121") require("safemain") diff --git a/compiler/tfldump/src/Dump.cpp b/compiler/tfldump/src/Dump.cpp index 4388fcd..a345039 100644 --- a/compiler/tfldump/src/Dump.cpp +++ b/compiler/tfldump/src/Dump.cpp @@ -15,7 +15,7 @@ */ #include -#include +#include #include "Read.h" #include "OpPrinter.h" diff --git a/compiler/tfldump/src/OpPrinter.cpp b/compiler/tfldump/src/OpPrinter.cpp index 2e8e713..f8777fa 100644 --- a/compiler/tfldump/src/OpPrinter.cpp +++ b/compiler/tfldump/src/OpPrinter.cpp @@ -359,6 +359,19 @@ public: } }; +class GeluPrinter : public OpPrinter +{ +public: + void options(const tflite::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_GeluOptions()) + { + os << " "; + os << "approximate(" << params->approximate() << ") "; + } + } +}; + class IfPrinter : public OpPrinter { public: @@ -630,6 +643,8 @@ public: os << "Padding(" << params->padding() << ") "; os << "Stride.W(" << params->stride_w() << ") "; os << "Stride.H(" << params->stride_h() << ") "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; os << std::endl; } } @@ -746,6 +761,7 @@ OpPrinterRegistry::OpPrinterRegistry() // There is no Option for FLOOR_MOD _op_map[tflite::BuiltinOperator_FULLY_CONNECTED] = make_unique(); _op_map[tflite::BuiltinOperator_GATHER] = make_unique(); + _op_map[tflite::BuiltinOperator_GELU] = make_unique(); _op_map[tflite::BuiltinOperator_IF] = make_unique(); _op_map[tflite::BuiltinOperator_L2_POOL_2D] = make_unique(); _op_map[tflite::BuiltinOperator_L2_NORMALIZATION] = make_unique(); diff --git a/compiler/tfldump/src/Read.cpp b/compiler/tfldump/src/Read.cpp index 454e3a8..7fa3d81 100644 --- a/compiler/tfldump/src/Read.cpp +++ b/compiler/tfldump/src/Read.cpp @@ -16,7 +16,7 @@ #include "Read.h" -#include +#include #include #include diff --git a/compiler/tflite2circle/CMakeLists.txt b/compiler/tflite2circle/CMakeLists.txt index a317a63..73639f3 100644 --- a/compiler/tflite2circle/CMakeLists.txt +++ b/compiler/tflite2circle/CMakeLists.txt @@ -1,8 +1,8 @@ nnas_include(TargetRequire) unset(REQUIRED_TARGETS) -list(APPEND REQUIRED_TARGETS mio_tflite280) -list(APPEND REQUIRED_TARGETS mio_circle04) +list(APPEND REQUIRED_TARGETS mio_tflite2121) +list(APPEND REQUIRED_TARGETS mio_circle06) TargetRequire_Return(${REQUIRED_TARGETS}) set(DRIVER "driver/Driver.cpp") @@ -13,9 +13,9 @@ target_include_directories(tflite2circle PRIVATE src) target_link_libraries(tflite2circle arser) target_link_libraries(tflite2circle foder) target_link_libraries(tflite2circle safemain) -target_link_libraries(tflite2circle mio_tflite280) -target_link_libraries(tflite2circle mio_tflite280_helper) -target_link_libraries(tflite2circle mio_circle04) +target_link_libraries(tflite2circle mio_tflite2121) +target_link_libraries(tflite2circle mio_tflite2121_helper) +target_link_libraries(tflite2circle mio_circle06) target_link_libraries(tflite2circle vconone) target_link_libraries(tflite2circle nncc_coverage) diff --git a/compiler/tflite2circle/requires.cmake b/compiler/tflite2circle/requires.cmake index 3db9a2f..c5528c2 100644 --- a/compiler/tflite2circle/requires.cmake +++ b/compiler/tflite2circle/requires.cmake @@ -1,6 +1,6 @@ require("arser") require("foder") -require("mio-tflite280") -require("mio-circle04") +require("mio-tflite2121") +require("mio-circle06") require("safemain") require("vconone") diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions.h index 8149197..5ed88ce 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions.h +++ b/compiler/tflite2circle/src/BuildBuiltinOptions.h @@ -46,6 +46,7 @@ #include "BuildBuiltinOptions/FullyConnectedOptions.h" #include "BuildBuiltinOptions/GatherOptions.h" #include "BuildBuiltinOptions/GatherNdOptions.h" +#include "BuildBuiltinOptions/GeluOptions.h" #include "BuildBuiltinOptions/GreaterOptions.h" #include "BuildBuiltinOptions/GreaterEqualOptions.h" #include "BuildBuiltinOptions/IfOptions.h" diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.cpp new file mode 100644 index 0000000..92fcd60 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GeluOptions.h" + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_GeluOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op) +{ + auto *tflite_builtin_options = op->builtin_options_as_GeluOptions(); + assert(tflite_builtin_options); + circle::GeluOptionsBuilder builtin_options_builder{fb}; + builtin_options_builder.add_approximate(tflite_builtin_options->approximate()); + return builtin_options_builder.Finish(); +} + +} // namespace tflite2circle diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.h new file mode 100644 index 0000000..76955d7 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GeluOptions.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_GELU_OPTIONS_H__ +#define __BBO_GELU_OPTIONS_H__ + +#include +#include + +namespace tflite2circle +{ + +flatbuffers::Offset +build_circle_GeluOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op); + +} // namespace tflite2circle + +#endif // __BBO_GELU_OPTIONS_H__ diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp index 301f2c4..0873170 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp @@ -31,6 +31,8 @@ build_circle_TransposeConvOptions(flatbuffers::FlatBufferBuilder &fb, const tfli builtin_options_builder.add_padding(get_circle_padding(tflite_builtin_options->padding())); builtin_options_builder.add_stride_w(tflite_builtin_options->stride_w()); builtin_options_builder.add_stride_h(tflite_builtin_options->stride_h()); + builtin_options_builder.add_fused_activation_function( + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp index ac017b8..899bc84 100644 --- a/compiler/tflite2circle/src/CircleModel.cpp +++ b/compiler/tflite2circle/src/CircleModel.cpp @@ -22,7 +22,7 @@ #include "CircleModel.h" #include "DataLookup.h" -#include +#include namespace tflite2circle { @@ -344,13 +344,31 @@ template <> void Offset::build(const TFLFlatBufVec *tflite_fla circle::OperatorCodeBuilder operator_code_builder{*_fb}; auto de_code = it->deprecated_builtin_code(); auto bt_code = it->builtin_code(); - auto cir_de_code = get_circle_builtin_code(de_code); - auto cir_bt_code = get_circle_builtin_code(bt_code); - // correct bt_code where bt_code == 0 for old tflite format - if (cir_bt_code == 0) - cir_bt_code = static_cast(cir_de_code); - operator_code_builder.add_deprecated_builtin_code(cir_de_code); - operator_code_builder.add_builtin_code(cir_bt_code); + + // There are two builtin codes (deprecated_builtin, (extended) builtin) + // deprecated builtin code uses 0~126 + // extended builtin code uses 127~ + // NOTE 127 = BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES + if (de_code >= 0 and de_code < 127) + { + // Use deprecated builtin opcode. + auto cir_de_code = get_circle_builtin_code(de_code); + auto cir_bt_code = get_circle_builtin_code(bt_code); + // correct bt_code where bt_code == 0 for old tflite format + if (cir_bt_code == 0) + cir_bt_code = static_cast(cir_de_code); + operator_code_builder.add_deprecated_builtin_code(cir_de_code); + operator_code_builder.add_builtin_code(cir_bt_code); + } + else + { + // Use extended builtin opcode + // Set 127 (PLACEHOLDER_FOR_GREATER_OP_CODES) for deprecated builtin code + auto cir_bt_code = get_circle_builtin_code(bt_code); + operator_code_builder.add_deprecated_builtin_code( + tflite::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES); + operator_code_builder.add_builtin_code(cir_bt_code); + } operator_code_builder.add_custom_code(custom_code); operator_code_builder.add_version(it->version()); auto code = operator_code_builder.Finish(); diff --git a/compiler/tflite2circle/src/TFLBuiltinOptions.lst b/compiler/tflite2circle/src/TFLBuiltinOptions.lst index 9cbf803..ac77c9a 100644 --- a/compiler/tflite2circle/src/TFLBuiltinOptions.lst +++ b/compiler/tflite2circle/src/TFLBuiltinOptions.lst @@ -28,6 +28,7 @@ TFL_BUILTIN_OPTIONS(MulOptions) TFL_BUILTIN_OPTIONS(PadOptions) TFL_BUILTIN_OPTIONS(PadV2Options) TFL_BUILTIN_OPTIONS(GatherOptions) +TFL_BUILTIN_OPTIONS(GeluOptions) TFL_BUILTIN_OPTIONS(BatchToSpaceNDOptions) TFL_BUILTIN_OPTIONS(SpaceToBatchNDOptions) TFL_BUILTIN_OPTIONS(TransposeOptions) diff --git a/compiler/tflite2circle/src/TFLOperator.lst b/compiler/tflite2circle/src/TFLOperator.lst index 72a29fc..b7db49b 100644 --- a/compiler/tflite2circle/src/TFLOperator.lst +++ b/compiler/tflite2circle/src/TFLOperator.lst @@ -151,3 +151,4 @@ TFL_OPERATOR(READ_VARIABLE) TFL_OPERATOR(ASSIGN_VARIABLE) TFL_OPERATOR(BROADCAST_ARGS) TFL_OPERATOR(RANDOM_STANDARD_NORMAL) +TFL_OPERATOR(GELU) diff --git a/compiler/vconone/CMakeLists.txt b/compiler/vconone/CMakeLists.txt index 38e8686..c5c9be2 100644 --- a/compiler/vconone/CMakeLists.txt +++ b/compiler/vconone/CMakeLists.txt @@ -1,5 +1,5 @@ if (NOT VCONONE_VERSION) - set(VCONONE_VERSION 0x0000000000160001) + set(VCONONE_VERSION 0x0000000000190001) # NOTE order is [build patch minor major] # if VCONONE_VERSION is set with -D option, it will be cached # you may have to remove cache file if you remove -D option diff --git a/compiler/vconone/src/version.cpp b/compiler/vconone/src/version.cpp index cebf7d9..fd34e92 100644 --- a/compiler/vconone/src/version.cpp +++ b/compiler/vconone/src/version.cpp @@ -54,7 +54,7 @@ std::string get_string(void) std::string get_copyright(void) { std::string str; - str = "Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; + str = "Copyright (c) 2020-2023 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; str += "Licensed under the Apache License, Version 2.0\r\n"; str += "https://github.com/Samsung/ONE"; return str; diff --git a/compiler/visq-unittest/CMakeLists.txt b/compiler/visq-unittest/CMakeLists.txt index 4eefa8d..7cc0bc0 100644 --- a/compiler/visq-unittest/CMakeLists.txt +++ b/compiler/visq-unittest/CMakeLists.txt @@ -53,14 +53,6 @@ add_custom_target(visq_unittest ALL DEPENDS ${VISQ_TEST_DEPS}) # Use Python in venv to run unittest with pydot module add_test( NAME visq_unittest - COMMAND ${NNCC_OVERLAY_DIR}/venv_2_8_0/bin/python -m unittest + COMMAND ${NNCC_OVERLAY_DIR}/venv_2_12_1/bin/python -m unittest WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) - -if(ONE_UBUNTU_CODENAME_JAMMY) - add_test( - NAME visq_210_unittest - COMMAND ${NNCC_OVERLAY_DIR}/venv_2_10_1/bin/python -m unittest - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) -endif(ONE_UBUNTU_CODENAME_JAMMY) diff --git a/compiler/visq-unittest/test/testDotBuilder.py b/compiler/visq-unittest/test/testDotBuilder.py index b657d60..d0e6ada 100644 --- a/compiler/visq-unittest/test/testDotBuilder.py +++ b/compiler/visq-unittest/test/testDotBuilder.py @@ -15,7 +15,6 @@ import unittest import pydot -from pathlib import Path from visqlib.DotBuilder import DotBuilder from test.Resources import fp32_model_dir diff --git a/compiler/visq-unittest/test/testQErrorComputer.py b/compiler/visq-unittest/test/testQErrorComputer.py index 1c6b185..3065b71 100644 --- a/compiler/visq-unittest/test/testQErrorComputer.py +++ b/compiler/visq-unittest/test/testQErrorComputer.py @@ -17,10 +17,12 @@ import unittest import tempfile import numpy as np import os +import json from visqlib.QErrorComputer import MPEIRComputer from visqlib.QErrorComputer import MSEComputer from visqlib.QErrorComputer import TAEComputer +from visqlib.QErrorComputer import SRMSEComputer class VisqQErrorComputerTest(unittest.TestCase): @@ -35,31 +37,43 @@ class VisqQErrorComputerTest(unittest.TestCase): self.fq_dir.cleanup() def _setUpSingleTensorData(self): - with open(self.fp32_dir.name + '/tensors.txt', 'w') as f: - f.write('test') - with open(self.fq_dir.name + '/tensors.txt', 'w') as f: - f.write('test') + tensor_id = {} + tensor_id['test'] = 0 + with open(self.fp32_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + with open(self.fq_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + scales = {} + scales['test'] = 2.0 + with open(self.fq_dir.name + '/scales.txt', 'w') as f: + json.dump(scales, f) os.mkdir(self.fp32_dir.name + '/0') os.mkdir(self.fq_dir.name + '/0') test_data = np.zeros(16) - np.save(self.fp32_dir.name + '/0/test.npy', test_data) - np.save(self.fq_dir.name + '/0/test.npy', test_data) + np.save(self.fp32_dir.name + '/0/0.npy', test_data) + np.save(self.fq_dir.name + '/0/0.npy', test_data) def _setUpTwoTensorData(self): - with open(self.fp32_dir.name + '/tensors.txt', 'w') as f: - f.write('test') - with open(self.fq_dir.name + '/tensors.txt', 'w') as f: - f.write('test') + tensor_id = {} + tensor_id['test'] = 0 + with open(self.fp32_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + with open(self.fq_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + scales = {} + scales['test'] = 2.0 + with open(self.fq_dir.name + '/scales.txt', 'w') as f: + json.dump(scales, f) os.mkdir(self.fp32_dir.name + '/0') os.mkdir(self.fp32_dir.name + '/1') os.mkdir(self.fq_dir.name + '/0') os.mkdir(self.fq_dir.name + '/1') test_data_one = np.ones(16) test_data_zero = np.zeros(16) - np.save(self.fp32_dir.name + '/0/test.npy', test_data_one) - np.save(self.fp32_dir.name + '/1/test.npy', test_data_zero) - np.save(self.fq_dir.name + '/0/test.npy', test_data_zero) - np.save(self.fq_dir.name + '/1/test.npy', test_data_zero) + np.save(self.fp32_dir.name + '/0/0.npy', test_data_one) + np.save(self.fp32_dir.name + '/1/0.npy', test_data_zero) + np.save(self.fq_dir.name + '/0/0.npy', test_data_zero) + np.save(self.fq_dir.name + '/1/0.npy', test_data_zero) # Golden: (1 + 0) / 2 = 0.5 for MSE def _setUpDifferentTensorData(self): @@ -68,30 +82,38 @@ class VisqQErrorComputerTest(unittest.TestCase): # NOTE When does this happen? # This case can happen because visq ignores nodes that do not affect qerrors. # For example, RESHAPE Op does not affect qerrors, so its fq data is not dumped, - # although it is listed in 'tensors.txt'. - with open(self.fp32_dir.name + '/tensors.txt', 'w') as f: - f.writelines(['test\n', 'test2']) - with open(self.fq_dir.name + '/tensors.txt', 'w') as f: - f.writelines(['test\n', 'test2']) + # although it is listed in 'tensors.json'. + tensor_id = {} + tensor_id['test'] = 0 + tensor_id['test2'] = 1 + with open(self.fp32_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + with open(self.fq_dir.name + '/tensors.json', 'w') as f: + json.dump(tensor_id, f) + scales = {} + scales['test'] = 2.0 + scales['test2'] = 1.0 + with open(self.fq_dir.name + '/scales.txt', 'w') as f: + json.dump(scales, f) os.mkdir(self.fp32_dir.name + '/0') os.mkdir(self.fq_dir.name + '/0') test_data = np.zeros(16) - np.save(self.fp32_dir.name + '/0/test.npy', test_data) - np.save(self.fp32_dir.name + '/0/test2.npy', test_data) - np.save(self.fq_dir.name + '/0/test.npy', test_data) + np.save(self.fp32_dir.name + '/0/0.npy', test_data) + np.save(self.fp32_dir.name + '/0/1.npy', test_data) + np.save(self.fq_dir.name + '/0/0.npy', test_data) def test_MPEIR(self): self._setUpSingleTensorData() computer = MPEIRComputer(self.fp32_dir.name, self.fq_dir.name) - qmap = computer.run() + qmap, _, _ = computer.run() self.assertAlmostEqual(0.0, qmap['test']) def test_MPEIR_different_tensors(self): self._setUpDifferentTensorData() computer = MPEIRComputer(self.fp32_dir.name, self.fq_dir.name) - qmap = computer.run() + qmap, _, _ = computer.run() self.assertAlmostEqual(0.0, qmap['test']) def test_MSE(self): @@ -145,6 +167,33 @@ class VisqQErrorComputerTest(unittest.TestCase): self.assertAlmostEqual(8.0, qmap['test']) self.assertAlmostEqual(16.0, qmax) + def test_SRMSE(self): + self._setUpSingleTensorData() + + computer = SRMSEComputer(self.fp32_dir.name, self.fq_dir.name) + qmap, qmin, qmax = computer.run() + self.assertAlmostEqual(0.0, qmap['test']) + self.assertAlmostEqual(0.0, qmin) + self.assertAlmostEqual(0.0, qmax) + + def test_SRMSE_different_options(self): + self._setUpDifferentTensorData() + + computer = SRMSEComputer(self.fp32_dir.name, self.fq_dir.name) + qmap, qmin, qmax = computer.run() + self.assertAlmostEqual(0.0, qmap['test']) + self.assertAlmostEqual(0.0, qmin) + self.assertAlmostEqual(0.0, qmax) + + def test_SRMSE_two(self): + self._setUpTwoTensorData() + computer = SRMSEComputer(self.fp32_dir.name, self.fq_dir.name) + qmap, qmin, qmax = computer.run() + # Golden: sqrt(Golden of MSE) / scale = sqrt(0.5) / 2 + self.assertAlmostEqual(np.sqrt(0.5) / 2, qmap['test']) + self.assertAlmostEqual(0.0, qmin) + self.assertAlmostEqual(np.sqrt(0.5) / 2, qmax) + if __name__ == '__main__': unittest.main() diff --git a/compiler/visq/CMakeLists.txt b/compiler/visq/CMakeLists.txt index 0dd1b78..6d13a7e 100644 --- a/compiler/visq/CMakeLists.txt +++ b/compiler/visq/CMakeLists.txt @@ -44,7 +44,7 @@ foreach(VISQ_PYTHON_FILE IN ITEMS ${VISQ_PYTHON_FILES}) add_custom_command(OUTPUT ${VISQ_PYTHON_FILE_BIN} COMMAND ${CMAKE_COMMAND} -E copy "${VISQ_PYTHON_FILE_SRC}" "${VISQ_PYTHON_FILE_BIN}" - DEPENDS ${VISQ_PYTHON_SRC} + DEPENDS ${VISQ_PYTHON_FILE_SRC} COMMENT "Generate ${VISQ_PYTHON_FILE_BIN}" ) diff --git a/compiler/visq/visq b/compiler/visq/visq index 02f63ab..6c3b94d 100644 --- a/compiler/visq/visq +++ b/compiler/visq/visq @@ -34,7 +34,7 @@ from shutil import copyfile from pathlib import Path from visqlib.Palette import YLORRD9Palette -from visqlib.QErrorComputer import MPEIRComputer, MSEComputer, TAEComputer +from visqlib.QErrorComputer import MPEIRComputer, MSEComputer, TAEComputer, SRMSEComputer from visqlib.Util import valid_attr, pretty_float from visqlib.DotBuilder import DotBuilder @@ -77,6 +77,11 @@ def _get_parser(): help="Path to the output json file (qerror metric = TAE).", required=False) parser.add_argument( + "--srmse_output", + type=str, + help="Path to the output json file (qerror metric = SRMSE).", + required=False) + parser.add_argument( "--dump_dot_graph", action="store_true", help="Dump dot graph.", required=False) parser.add_argument( "-b", @@ -91,7 +96,7 @@ def _get_parser(): def _verify_args(args): """Verify the given arguments""" - valid_outputs = ['mpeir_output', 'mse_output', 'tae_output'] + valid_outputs = ['mpeir_output', 'mse_output', 'tae_output', 'srmse_output'] # Check if at least one output option is given num_outputs = 0 @@ -114,22 +119,24 @@ def _run_dalgona(model, data, analysis, save_dir): cmd += ['--analysis_args', str(save_dir)] try: - subprocess.run(cmd, capture_output=True, check=True) + subprocess.run(cmd, capture_output=True, check=True, universal_newlines=True) except subprocess.CalledProcessError as e: print('Error raised while running the below command') print(' '.join(cmd)) - print(e.output) + print(e.stderr) raise # Generate h5 file that contains a dataset of a single batch # This is for batch execution of visq -def gen_batch_h5(inputs_data, inputs_path): +def gen_batch_h5(inputs_data, inputs_path, rawData): # Create h5 file output_path = inputs_path + "/inputs.h5" h5_file = h5.File(output_path, 'w') group = h5_file.create_group("value") group.attrs['desc'] = "Input data" + if rawData: + group.attrs['rawData'] = '1' for i in range(len(inputs_data)): sample = group.create_group(str(i)) @@ -151,7 +158,7 @@ def advance_on_data(fp32_model, fq_model, data, computers): tempfile.TemporaryDirectory() as fq_dir: _run_dalgona(fp32_model, data, dump_fp32_py, fp32_dir) - copyfile(fp32_dir + '/tensors.txt', fq_dir + '/tensors.txt') + copyfile(fp32_dir + '/tensors.json', fq_dir + '/tensors.json') _run_dalgona(fq_model, data, dump_fq_py, fq_dir) for metric_key in computers: @@ -162,22 +169,23 @@ def _run_batch(fp32_model, fq_model, data, computers, batch_size): with tempfile.TemporaryDirectory() as inputs_dir: with h5.File(data, 'r') as f: dataset = f['value'] - + rawData = dataset.attrs.__contains__( + 'rawData') and dataset.attrs['rawData'] == '1' inputs = [] for data_index in dataset: cur_inputs = [] for input_index in dataset[data_index]: d = dataset[data_index][input_index][:] - cur_inputs.append(np.array(d, np.float32)) + cur_inputs.append(d) inputs.append(cur_inputs) if len(inputs) >= batch_size: - input_path = gen_batch_h5(inputs, inputs_dir) + input_path = gen_batch_h5(inputs, inputs_dir, rawData) advance_on_data(fp32_model, fq_model, input_path, computers) inputs = [] if len(inputs) > 0: - input_path = gen_batch_h5(inputs, inputs_dir) + input_path = gen_batch_h5(inputs, inputs_dir, rawData) advance_on_data(fp32_model, fq_model, input_path, computers) @@ -190,11 +198,11 @@ def _fake_quantize(input_model, output_model): cmd += [str(output_model)] try: - subprocess.run(cmd, check=True) + subprocess.run(cmd, check=True, universal_newlines=True) except subprocess.CalledProcessError as e: print('Error raised while running the below command') print(' '.join(cmd)) - print(e.output) + print(e.stderr) raise @@ -262,12 +270,7 @@ def run_on_data_batchwise(fp32_model, q_model, data, dump_dot_graph, computers, for metric_key in computers: cur_computer = computers[metric_key][0] output = computers[metric_key][1] - if metric_key == 'MPEIR': - qerror_map = cur_computer.get_final_result() - q_min = 0.0 - q_max = 1.0 - elif metric_key == 'MSE' or metric_key == 'TAE': - qerror_map, q_min, q_max = cur_computer.get_final_result() + qerror_map, q_min, q_max = cur_computer.get_final_result() palette = YLORRD9Palette(qerror_min=q_min, qerror_max=q_max) result = _build_json( @@ -304,7 +307,7 @@ def run_on_data(fp32_model, q_model, data, dump_dot_graph, computers): _run_dalgona(fp32_model, data, dump_fp32_py, fp32_dir) # Copy list of dumped tensors - copyfile(fp32_dir + '/tensors.txt', fq_dir + '/tensors.txt') + copyfile(fp32_dir + '/tensors.json', fq_dir + '/tensors.json') # Step 3. Run dalgona to dump intermediate FMs in fq model _run_dalgona(fq_model, data, dump_fq_py, fq_dir) @@ -314,12 +317,7 @@ def run_on_data(fp32_model, q_model, data, dump_dot_graph, computers): cur_computer = computers[metric_key][0] output = computers[metric_key][1] cur_computer.advance_on(fp32_dir, fq_dir) - if metric_key == 'MPEIR': - qerror_map = cur_computer.get_final_result() - q_min = 0.0 - q_max = 1.0 - elif metric_key == 'MSE' or metric_key == 'TAE': - qerror_map, q_min, q_max = cur_computer.get_final_result() + qerror_map, q_min, q_max = cur_computer.get_final_result() palette = YLORRD9Palette(qerror_min=q_min, qerror_max=q_max) result = _build_json( @@ -365,6 +363,9 @@ def main(): if args.tae_output: computers['TAE'] = (TAEComputer(None, None), args.tae_output) + if args.srmse_output: + computers['SRMSE'] = (SRMSEComputer(None, None), args.srmse_output) + if batch_size == None: run_on_data(fp32_model, q_model, data, dump_dot_graph, computers) else: diff --git a/compiler/visq/visqlib/DumpFP32FM.py b/compiler/visq/visqlib/DumpFP32FM.py index d5b7545..14dc900 100644 --- a/compiler/visq/visqlib/DumpFP32FM.py +++ b/compiler/visq/visqlib/DumpFP32FM.py @@ -16,9 +16,9 @@ # NOTE This script runs on dalgona import numpy as np +import json from pathlib import Path -from Util import to_filename # Dump FP32 model's intermediate FM data and their names @@ -28,27 +28,37 @@ from Util import to_filename # # After # self._dir/ -# tensors.txt -# .npy -# NOTE TENSOR_NAME is transformed by to_filename +# tensors.json +# .npy +# NOTE tensors.json has a dictionary {TENSOR_NAME -> TENSOR_ID} class DumpFP32FM: def StartAnalysis(self, args): self._dir = Path(args) self._num_data = 0 - self._tensor_names = set() + # Dict {tensor_name -> tid} + self._tname_to_tid = dict() + self._tensor_count = 0 def EndNetworkExecution(self, outputs): self._num_data += 1 - def DefaultOpPost(self, name, opcode, inputs, output): - # Save intermediate FM into tensor_name.npy + def DefaultOpPost(self, name, opcode, inputs, outputs): + # Save intermediate FM into .npy data_path = self._dir / str(self._num_data) data_path.mkdir(parents=False, exist_ok=True) - np.save(str(data_path / to_filename(name)), output['data']) - self._tensor_names.add(name) + for output in outputs: + name = output['name'] + data = output['data'] + if name in self._tname_to_tid: + tid = self._tname_to_tid[name] + else: + tid = self._tensor_count + self._tname_to_tid[name] = tid + self._tensor_count += 1 + + np.save(str(data_path / str(tid)), data) def EndAnalysis(self): - # Save tensor names line by line - with open(self._dir / 'tensors.txt', 'w') as f: - for name in self._tensor_names: - f.write("%s\n" % name) + # Save tensor name : tensor id pairs + with open(self._dir / 'tensors.json', 'w') as f: + json.dump(self._tname_to_tid, f, indent=2) diff --git a/compiler/visq/visqlib/DumpFakeQuantFM.py b/compiler/visq/visqlib/DumpFakeQuantFM.py index 0248428..b8dde38 100644 --- a/compiler/visq/visqlib/DumpFakeQuantFM.py +++ b/compiler/visq/visqlib/DumpFakeQuantFM.py @@ -16,9 +16,9 @@ # NOTE This script runs on dalgona import numpy as np +import json from pathlib import Path -from Util import to_filename # Fake-quantized Op has the postfix of fq_postfix # TODO Remove coupling with fake quantization codes @@ -39,28 +39,46 @@ def _name_before_fq(name): # # Before # self._dir/ -# tensors.txt +# tensors.json # # After # self._dir/ -# tensors.txt -# .npy -# NOTE TENSOR_NAME is transformed by to_filename +# tensors.json +# .npy +# NOTE tensors.json has a dictionary {TENSOR_NAME -> TENSOR_ID} class DumpFakeQuantFM: def StartAnalysis(self, args): self._dir = Path(args) self._num_data = 0 - with open(self._dir / 'tensors.txt') as f: - self._target_tensors = set([line.rstrip() for line in f]) + with open(self._dir / 'tensors.json') as f: + self._tname_to_tid = json.load(f) + self._scale_map = {} def EndNetworkExecution(self, outputs: list): self._num_data += 1 # TODO Use DequantizePost when dalgona supports it - def DefaultOpPost(self, name, opcode, inputs, output): + def DefaultOpPost(self, name, opcode, inputs, outputs): if opcode == 'Dequantize': - orig_name = _name_before_fq(name) - if orig_name in self._target_tensors: - data_path = self._dir / str(self._num_data) - data_path.mkdir(parents=False, exist_ok=True) - np.save(str(data_path / to_filename(orig_name)), output['data']) + for output in outputs: + name = output['name'] + data = output['data'] + orig_name = _name_before_fq(name) + if orig_name in self._tname_to_tid: + tid = self._tname_to_tid[orig_name] + data_path = self._dir / str(self._num_data) + data_path.mkdir(parents=False, exist_ok=True) + np.save(str(data_path / str(tid)), data) + # Save scales (scale is fixed, so saving once) + if orig_name not in self._scale_map: + assert len(inputs) == 1 + assert 'quantparam' in inputs[0] + assert 'scale' in inputs[0]['quantparam'] + assert len(inputs[0]['quantparam']['scale']) == 1 + scale = inputs[0]['quantparam']['scale'][0] + self._scale_map[orig_name] = scale + + def EndAnalysis(self): + # Dump saved scales into scales.txt + with open(self._dir / 'scales.txt', 'w') as f: + json.dump(self._scale_map, f) diff --git a/compiler/visq/visqlib/QErrorComputer.py b/compiler/visq/visqlib/QErrorComputer.py index c60556f..46ba3e7 100644 --- a/compiler/visq/visqlib/QErrorComputer.py +++ b/compiler/visq/visqlib/QErrorComputer.py @@ -15,44 +15,45 @@ import os import glob import numpy as np +import json from pathlib import Path -from visqlib.Util import to_filename +from collections import defaultdict class QErrorComputer: def __init__(self, fp32_dir, fq_dir): self._fp32_dir = fp32_dir self._fq_dir = fq_dir - self.qerror_map = dict() + self.qerror_map = defaultdict(float) self._num_processed_data = 0 def collect_data_path(self, fp32_dir, fq_dir): # Assumption: FM data are saved as follows # # fp32_dir/ - # tensors.txt + # tensors.json # / - # .npy + # .npy # # fq_dir/ - # tensors.txt + # tensors.json # / - # .npy + # .npy + # + # NOTE tensors.json has a dictionary {TENSOR_NAME -> TENSOR_ID} self._num_data = len(list(filter(os.path.isdir, glob.glob(fp32_dir + '/*')))) if self._num_data != len(list(filter(os.path.isdir, glob.glob(fq_dir + '/*')))): raise RuntimeError("Number of data mistmatches") self._num_processed_data += self._num_data - self._filename_to_tensor = dict() - with open(Path(fp32_dir) / 'tensors.txt') as f: - tensors = set([line.rstrip() for line in f]) - for tensor in tensors: - # Check if filename is unique - # Fix name finding logic unless - assert to_filename(tensor) not in self._filename_to_tensor - self._filename_to_tensor[to_filename(tensor)] = tensor + self._tid_to_tname = dict() # {tensor id -> tensor name} + with open(Path(fp32_dir) / 'tensors.json') as f: + tname_to_tid = json.load(f) + + for tname, tid in tname_to_tid.items(): + self._tid_to_tname[tid] = tname # Save paths to fp32 data and fq data for each tensor # dict @@ -69,8 +70,8 @@ class QErrorComputer: fq_data_path = fq_dir + '/' + str(data_idx) + '/' + fp32_path.with_suffix( '.npy').name fq_path = Path(fq_data_path) - filename = fp32_path.stem - tensor_name = self._filename_to_tensor[filename] + tid = int(fp32_path.stem) + tensor_name = self._tid_to_tname[tid] # Only save the tensors which have both fp32 data and fq data if fq_path.is_file() and fp32_path.is_file(): @@ -112,17 +113,19 @@ class MPEIRComputer(QErrorComputer): # To prevent this, relaxed PEIR with epsilon(10^(-6)) is used. rPEIR = PEAK_ERROR / (INTERVAL + 0.000001) - if tensor_name in self.qerror_map: - self.qerror_map[tensor_name] += rPEIR - else: - self.qerror_map[tensor_name] = rPEIR + self.qerror_map[tensor_name] += rPEIR + # Return + # qerror_map (dict: tensor_name(string) -> qerror(float)) + # qerror_min (float) + # qerror_max (float) def get_final_result(self): qerror_map = dict() for tensor_name, acc in self.qerror_map.items(): qerror_map[tensor_name] = acc / self._num_processed_data - return qerror_map + # Fixed qerror_min (0), qerror_max (1) + return qerror_map, 0.0, 1.0 def run(self): self.advance_on(self._fp32_dir, self._fq_dir) @@ -145,14 +148,15 @@ class MSEComputer(QErrorComputer): MSE = np.square(fp32_data - fq_data).mean() - if tensor_name in self.qerror_map: - self.qerror_map[tensor_name] += MSE - else: - self.qerror_map[tensor_name] = MSE + self.qerror_map[tensor_name] += MSE self.qerror_min = min(MSE, self.qerror_min) self.qerror_max = max(MSE, self.qerror_max) + # Return + # qerror_map (dict: tensor_name(string) -> qerror(float)) + # qerror_min (float) + # qerror_max (float) def get_final_result(self): qerror_map = dict() for tensor_name, acc in self.qerror_map.items(): @@ -181,14 +185,15 @@ class TAEComputer(QErrorComputer): #total absolute error total_error = np.sum(np.abs(fp32_data - fq_data)) - if tensor_name in self.qerror_map: - self.qerror_map[tensor_name] += total_error - else: - self.qerror_map[tensor_name] = total_error + self.qerror_map[tensor_name] += total_error self.qerror_min = min(total_error, self.qerror_min) self.qerror_max = max(total_error, self.qerror_max) + # Return + # qerror_map (dict: tensor_name(string) -> qerror(float)) + # qerror_min (float) + # qerror_max (float) def get_final_result(self): qerror_map = dict() for tensor_name, acc in self.qerror_map.items(): @@ -198,3 +203,52 @@ class TAEComputer(QErrorComputer): #total absolute error def run(self): self.advance_on(self._fp32_dir, self._fq_dir) return self.get_final_result() + + +# Scaled Root Mean Square Error (SRMSE) +# SRMSE = sqrt(MSE) / scale +class SRMSEComputer(QErrorComputer): + def __init__(self, fp32_dir, fq_dir): + super().__init__(fp32_dir, fq_dir) + if fq_dir != None: + self.scale_file = Path(fq_dir) / 'scales.txt' + + # Incrementally compute Qerror while traversing all data in fp32_dir and fq_dir + def advance_on(self, fp32_dir, fq_dir): + if fq_dir != None: + self.scale_file = Path(fq_dir) / 'scales.txt' + self._fq_dir = fq_dir + + data_paths = self.collect_data_path(fp32_dir, fq_dir) + + for tensor_name, data_path in data_paths.items(): + for (fp32_data_path, fq_data_path) in data_path: + fp32_data = np.load(fp32_data_path) + fq_data = np.load(fq_data_path) + + MSE = np.square(fp32_data - fq_data).mean() + + self.qerror_map[tensor_name] += MSE + + # Return + # qerror_map (dict: tensor_name(string) -> qerror(float)) + # qerror_min (float) + # qerror_max (float) + def get_final_result(self): + with open(self.scale_file) as f: + # scale_map: {tensor_name(str) -> scale(float)} + scale_map = json.load(f) + + qerror_max = 0.0 + qerror_map = dict() + for tensor_name, acc in self.qerror_map.items(): + MSE = acc / self._num_processed_data + SRMSE = np.sqrt(MSE) / scale_map[tensor_name] + qerror_map[tensor_name] = SRMSE + qerror_max = max(SRMSE, qerror_max) + + return qerror_map, 0.0, qerror_max + + def run(self): + self.advance_on(self._fp32_dir, self._fq_dir) + return self.get_final_result() diff --git a/compute/ARMComputeEx/src/runtime/CL/functions/CLSplitVEx.cpp b/compute/ARMComputeEx/src/runtime/CL/functions/CLSplitVEx.cpp index 73f5f6e..bca4d5c 100644 --- a/compute/ARMComputeEx/src/runtime/CL/functions/CLSplitVEx.cpp +++ b/compute/ARMComputeEx/src/runtime/CL/functions/CLSplitVEx.cpp @@ -174,7 +174,7 @@ void CLSplitVEx::configure(const ICLTensor *input, const ICLTensor *size_splits, // Extract output tensor info std::vector outputs_info; - for (auto &output : _outputs) + for (auto &&output : _outputs) { ARM_COMPUTE_ERROR_ON_NULLPTR(output); outputs_info.emplace_back(output->info()); diff --git a/compute/cker/CMakeLists.txt b/compute/cker/CMakeLists.txt index ce328b6..d464dcc 100644 --- a/compute/cker/CMakeLists.txt +++ b/compute/cker/CMakeLists.txt @@ -12,6 +12,10 @@ if(PROFILE_RUY) target_link_libraries(nnfw_lib_cker INTERFACE ruy_profiler) endif(PROFILE_RUY) +if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + target_compile_definitions(nnfw_lib_cker INTERFACE CKER_X86_PLATFORM) +endif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + target_include_directories(nnfw_lib_cker INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) # Workaround to avoid warning diff --git a/compute/cker/include/cker/PortableTensorUtils.h b/compute/cker/include/cker/PortableTensorUtils.h index 2a58a2e..7e4b01a 100644 --- a/compute/cker/include/cker/PortableTensorUtils.h +++ b/compute/cker/include/cker/PortableTensorUtils.h @@ -144,6 +144,60 @@ inline void PortableSymmetricQuantizeFloats(const float *values, const int size, } } +inline void PortableAsymmetricQuantizeFloats(const float *values, const int size, + int8_t *quantized_values, float *scaling_factor, + int32_t *offset) +{ + /* Copied from TensorFlow PortableAsymmetricQuantizeFloats */ + const int32_t kMinScale = -128; + const int32_t kMaxScale = 127; + const double qmin_double = kMinScale; + const double qmax_double = kMaxScale; + const auto minmax = std::minmax_element(values, values + size); + const double rmin = static_cast(std::min(0.0f, *minmax.first)); + const double rmax = static_cast(std::max(0.0f, *minmax.second)); + if (rmin == rmax) + { + memset(quantized_values, 0, size * sizeof(int8_t)); + *scaling_factor = 1; + *offset = 0; + return; + } + else + { + double scale = (rmax - rmin) / (qmax_double - qmin_double); + const double zero_point_from_min = qmin_double - rmin / scale; + const double zero_point_from_max = qmax_double - rmax / scale; + const double zero_point_from_min_error = std::abs(qmin_double) + std::abs(rmin / scale); + const double zero_point_from_max_error = std::abs(qmax_double) + std::abs(rmax / scale); + const double zero_point_double = zero_point_from_min_error < zero_point_from_max_error + ? zero_point_from_min + : zero_point_from_max; + int8_t nudged_zero_point = 0; + if (zero_point_double <= qmin_double) + { + nudged_zero_point = kMinScale; + } + else if (zero_point_double >= qmax_double) + { + nudged_zero_point = kMaxScale; + } + else + { + nudged_zero_point = static_cast(round(zero_point_double)); + } + *scaling_factor = scale; + *offset = nudged_zero_point; + } + const float scaling_factor_inv = 1.0f / *scaling_factor; + for (int i = 0; i < size; ++i) + { + const int32_t quantized_value = + static_cast(std::round(*offset + values[i] * scaling_factor_inv)); + quantized_values[i] = std::min(kMaxScale, std::max(kMinScale, quantized_value)); + } +} + inline void PortableMatrixBatchVectorMultiplyAccumulate(const int8_t *__restrict__ matrix, const int m_rows, const int m_cols, const int8_t *__restrict__ vectors, diff --git a/compute/cker/include/cker/Shape.h b/compute/cker/include/cker/Shape.h index 86caf7d..9269ce9 100644 --- a/compute/cker/include/cker/Shape.h +++ b/compute/cker/include/cker/Shape.h @@ -156,7 +156,7 @@ public: const int dimensions_count = std::distance(src_iterable.begin(), src_iterable.end()); Resize(dimensions_count); int32_t *data = DimsData(); - for (auto it : src_iterable) + for (auto &&it : src_iterable) { *data = it; ++data; diff --git a/compute/cker/include/cker/Types.h b/compute/cker/include/cker/Types.h index 495c894..3fd0cf5 100644 --- a/compute/cker/include/cker/Types.h +++ b/compute/cker/include/cker/Types.h @@ -258,9 +258,12 @@ struct FullyConnectedParams // uint8, etc, activation params. int32_t quantized_activation_min; int32_t quantized_activation_max; - // float activation params - no one use this params, but ruy might use them later. - // float float_activation_min; - // float float_activation_max; + // float activation params + float float_activation_min; + float float_activation_max; + // Mark the operands as cacheable if they are unchanging, e.g. weights. + bool lhs_cacheable; + bool rhs_cacheable; // FullyConnectedWeightsFormat weights_format; }; diff --git a/compute/cker/include/cker/eigen/eigen_gemm_eigen.h b/compute/cker/include/cker/eigen/eigen_gemm_eigen.h new file mode 100644 index 0000000..d4f8fc0 --- /dev/null +++ b/compute/cker/include/cker/eigen/eigen_gemm_eigen.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_EGIEN_EIGEN_GEMM_EIGEN_H__ +#define __NNFW_CKER_EGIEN_EIGEN_GEMM_EIGEN_H__ + +// See b/131835803: in TFLite code, because eigen_spatial_convolutions.h does +// #define Eigen EigenForTFLite, it is difficult to have any #include of Eigen +// headers in a header file, as that results in name classes (compilation +// errors) depending on the order in which these headers are #included. +// So we have moved the #include of Eigen here, in a .cc file, where we have +// control over the header #include sequence. +// #include "third_party/eigen3/Eigen/Core" +// #include "tensorflow/lite/kernels/cpu_backend_context.h" +// #include "tensorflow/lite/kernels/cpu_backend_gemm_params.h" +// #include "tensorflow/lite/kernels/internal/common.h" +// #include "cker/eigen/eigen_convolution_helpers.h" +#include "cker/operation/Common.h" +#include "cker/Types.h" + +#include + +namespace nnfw +{ +namespace cker +{ +namespace detail +{ + +// tensorflow/tensorflow/lite/kernels/cpu_backend_gemm_eigen.h and cpu_backend_gemm_eigen.cc +struct GemmImplUsingEigen +{ + static void Run(const MatrixParams &lhs_params, const float *lhs_data, + const MatrixParams &rhs_params, const float *rhs_data, + const MatrixParams &dst_params, float *dst_data, + const GemmParams ¶ms) + { + // This code assumes specific storage orders, encoded in these Eigen types. + // These assumptions have been checked by TF_LITE_ASSERT's in the public + // Gemm entry point already, before the implementation gets to this point. + using EigenMatrixMapRowMajorConst = + Eigen::Map>; + using EigenMatrixMapColMajorConst = + Eigen::Map>; + using EigenMatrixMapColMajorMutable = + Eigen::Map>; + + EigenMatrixMapRowMajorConst eigen_lhs(lhs_data, lhs_params.rows, lhs_params.cols); + EigenMatrixMapColMajorConst eigen_rhs(rhs_data, rhs_params.rows, rhs_params.cols); + EigenMatrixMapColMajorMutable eigen_dst(dst_data, dst_params.rows, dst_params.cols); + + if (rhs_params.cols == 1) + { + eigen_dst.col(0).noalias() = eigen_lhs * eigen_rhs.col(0); + } + else if (lhs_params.rows == 1) + { + eigen_dst.row(0).noalias() = eigen_lhs.row(0) * eigen_rhs; + } + else + { + eigen_dst.noalias() = eigen_lhs * eigen_rhs; + } + + if (params.bias) + { + BiasAndClamp(params.clamp_min, params.clamp_max, dst_params.rows, params.bias, + dst_params.rows * dst_params.cols, dst_data); + } + else + { + eigen_dst = eigen_dst.cwiseMin(params.clamp_max).cwiseMax(params.clamp_min); + } + } +}; + +} // namespace detail +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_EGIEN_EIGEN_GEMM_EIGEN_H__ diff --git a/compute/cker/include/cker/operation/Conv.h b/compute/cker/include/cker/operation/Conv.h index 7cd54dc..2572b51 100644 --- a/compute/cker/include/cker/operation/Conv.h +++ b/compute/cker/include/cker/operation/Conv.h @@ -207,6 +207,21 @@ private: std::vector _per_channel_output_multiplier; std::vector _per_channel_output_shift; }; + +struct ConvHybridTempArena +{ + ConvHybridTempArena(int batch_size, int input_size) + { + input_quantized.resize(input_size); + // TODO: Optimize the case of batch_size = 1 + input_scaling_factors.resize(batch_size); + input_offsets.resize(batch_size); + } + std::vector input_quantized; + std::vector input_scaling_factors; + std::vector input_offsets; +}; + } // namespace cker } // namespace nnfw diff --git a/compute/cker/include/cker/operation/DepthwiseConv.h b/compute/cker/include/cker/operation/DepthwiseConv.h index ed1f93d..c926ec4 100644 --- a/compute/cker/include/cker/operation/DepthwiseConv.h +++ b/compute/cker/include/cker/operation/DepthwiseConv.h @@ -26,6 +26,7 @@ #include "cker/operation/optimized/DepthwiseConvUint8.h" #include "cker/operation/optimized/integer_ops/DepthwiseConvInt8.h" #include "cker/operation/reference/integer_ops/DepthwiseConvUInt8.h" +#include "cker/operation/reference/integer_ops/DepthwiseConvHybrid.h" #include "cker/CpuBackendThreadpool.h" namespace nnfw diff --git a/compute/cker/include/cker/operation/Einsum.h b/compute/cker/include/cker/operation/Einsum.h index 6721a75..bb9f88f 100644 --- a/compute/cker/include/cker/operation/Einsum.h +++ b/compute/cker/include/cker/operation/Einsum.h @@ -274,7 +274,7 @@ public: } for (int i = 0; i < num_inputs; ++i) { - for (int label : free_labels[i]) + for (auto &&label : free_labels[i]) { result_labels.push_back(label); result_shape_dims.push_back(label_to_dim_sizes[label]); @@ -300,7 +300,7 @@ public: { // We inflated the output. Modify result labels accordingly. Labels inflated_labels; - for (int label : result_labels) + for (auto &&label : result_labels) { inflated_labels.insert(inflated_labels.end(), output_label_counts[label], label); } @@ -775,7 +775,7 @@ private: Shape inflated_shape; std::vector strided_shape_dims; std::vector inflated_shape_dims; - for (int label : labels) + for (auto &&label : labels) { const int32_t count = label_counts[label]; const int current_axis = diff --git a/compute/cker/include/cker/operation/FullyConnected.h b/compute/cker/include/cker/operation/FullyConnected.h index b7d27e8..71a2f19 100644 --- a/compute/cker/include/cker/operation/FullyConnected.h +++ b/compute/cker/include/cker/operation/FullyConnected.h @@ -21,6 +21,7 @@ #include #include "cker/operation/FullyConnectedDense16x1.h" #include "cker/operation/FullyConnectedSparse16x1.h" +#include "cker/operation/optimized/Gemm.h" #include "cker/Shape.h" #include "cker/Types.h" #include "cker/Utils.h" @@ -58,6 +59,42 @@ public: std::vector accum_scratch; }; +#if defined(CKER_X86_PLATFORM) + +// From tensorflow/tensorflow/lite/kernels/internal/optimized/optimized_ops.h +inline void FullyConnected(const FullyConnectedParams ¶ms, const Shape &input_shape, + const float *input_data, const Shape &weights_shape, + const float *weights_data, const Shape &, + const float *optional_bias_data, const Shape &output_shape, + float *output_data) +{ + const int dims_count = weights_shape.DimensionsCount(); + const int input_rows = weights_shape.Dims(dims_count - 1); + MatrixParams rhs_params; + rhs_params.order = Order::kColMajor; + rhs_params.rows = input_rows; + rhs_params.cols = input_shape.FlatSize() / input_rows; + rhs_params.cache_policy = optimized::DefaultCachePolicy(params.rhs_cacheable); + + MatrixParams lhs_params; + lhs_params.order = Order::kRowMajor; + lhs_params.cols = weights_shape.Dims(dims_count - 1); + lhs_params.rows = FlatSizeSkipDim(weights_shape, dims_count - 1); + lhs_params.cache_policy = optimized::DefaultCachePolicy(params.lhs_cacheable); + MatrixParams dst_params; + dst_params.order = Order::kColMajor; + dst_params.rows = output_shape.Dims(output_shape.DimensionsCount() - 1); + dst_params.cols = FlatSizeSkipDim(output_shape, output_shape.DimensionsCount() - 1); + GemmParams gemm_params; + gemm_params.bias = optional_bias_data; + gemm_params.clamp_min = params.float_activation_min; + gemm_params.clamp_max = params.float_activation_max; + optimized::Gemm(lhs_params, weights_data, rhs_params, input_data, dst_params, output_data, + gemm_params); +} + +#else // CKER_X86_PLATFORM + inline void FullyConnected(const FullyConnectedParams ¶ms, const Shape &input_shape, const float *input_data, const Shape &weights_shape, const float *weights_data, const Shape &, const float *bias_data, @@ -89,6 +126,8 @@ inline void FullyConnected(const FullyConnectedParams ¶ms, const Shape &inpu } } +#endif // CKER_X86_PLATFORM + inline void FullyConnected(const FullyConnectedParams ¶ms, const Shape &input_shape, const uint8_t *input_data, const Shape &filter_shape, const uint8_t *filter_data, const Shape &bias_shape, diff --git a/compute/cker/include/cker/operation/optimized/Gemm.h b/compute/cker/include/cker/operation/optimized/Gemm.h new file mode 100644 index 0000000..cfebef4 --- /dev/null +++ b/compute/cker/include/cker/operation/optimized/Gemm.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_OPTIMIZED_GEMM_H__ +#define __NNFW_CKER_OPTIMIZED_GEMM_H__ + +#include "cker/eigen/eigen_gemm_eigen.h" +#include "cker/Shape.h" +#include "cker/Types.h" + +#include + +namespace nnfw +{ +namespace cker +{ +namespace optimized +{ + +#if defined(CKER_X86_PLATFORM) + +/* From tensorflow/tensorflow/lite/kernels/cpu_backend_gemm_x86.h */ +template +struct GemmImplX86 +{ + static void Run(const MatrixParams &, const LhsScalar *, + const MatrixParams &, const RhsScalar *, + const MatrixParams &, DstScalar *, + const GemmParams &) + { + static_assert( + std::is_floating_point::value && std::is_floating_point::value && + std::is_floating_point::value && std::is_floating_point::value && + quantization_flavor != QuantizationFlavor::kFloatingPoint, + "GemmImplX86 does not supported types other than float yet."); + } +}; + +// For float, defer to eigen for now. +template <> struct GemmImplX86 +{ + static void Run(const MatrixParams &lhs_params, const float *lhs_data, + const MatrixParams &rhs_params, const float *rhs_data, + const MatrixParams &dst_params, float *dst_data, + const GemmParams ¶ms) + { + detail::GemmImplUsingEigen::Run(lhs_params, lhs_data, rhs_params, rhs_data, dst_params, + dst_data, params); + } +}; + +/* From tensorflow/tensorflow/lite/kernels/cpu_backend_gemm.h */ +/* GEMM dispatch implementation for x86. + */ +template +struct GemmImpl : GemmImplX86 +{ +}; + +/* From tensorflow/tensorflow/lite/kernels/cpu_backend_gemm.h */ +template +void Gemm(const MatrixParams &lhs_params, const LhsScalar *lhs_data, + const MatrixParams &rhs_params, const RhsScalar *rhs_data, + const MatrixParams &dst_params, DstScalar *dst_data, + const GemmParams ¶ms) +{ + // Generic case: dispatch to any backend as a general GEMM. + GemmImpl::Run( + lhs_params, lhs_data, rhs_params, rhs_data, dst_params, dst_data, params); +} + +// From tensorflow/tensorflow/lite/kernels/cpu_backend_gemm_params.h +inline CachePolicy DefaultCachePolicy(bool is_constant_data) +{ + return is_constant_data ? CachePolicy::kCacheIfLargeSpeedup : CachePolicy::kNeverCache; +} +#endif // CKER_X86_PLATFORM + +} // namespace optimized +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_OPTIMIZED_GEMM_H__ diff --git a/compute/cker/include/cker/operation/reference/Conv.h b/compute/cker/include/cker/operation/reference/Conv.h index 8bfd469..e316083 100644 --- a/compute/cker/include/cker/operation/reference/Conv.h +++ b/compute/cker/include/cker/operation/reference/Conv.h @@ -311,6 +311,91 @@ inline void Conv(const ConvParams ¶ms, const int32_t *output_multiplier, } } +// Slightly modified from tflite 2.13.0 HybridConvPerChannel +// im2col and im2col_shape are removed since it is not used in reference kernel. +inline void HybridConvPerChannel(const ConvParams ¶ms, float *scaling_factors_ptr, + const Shape &input_shape, const int8_t *input_data, + const Shape &filter_shape, const int8_t *filter_data, + const Shape &bias_shape, const float *bias_data, + const Shape &output_shape, float *output_data, + const float *per_channel_scale, const int32_t *input_offset) + +{ + const int stride_width = params.stride_width; + const int stride_height = params.stride_height; + const int dilation_width_factor = params.dilation_width_factor; + const int dilation_height_factor = params.dilation_height_factor; + const int pad_width = params.padding_values.width; + const int pad_height = params.padding_values.height; + const float output_activation_min = params.float_activation_min; + const float output_activation_max = params.float_activation_max; + assert(input_shape.DimensionsCount() == 4); + assert(filter_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + const int batches = MatchingDim(input_shape, 0, output_shape, 0); + const int input_depth = input_shape.Dims(3); + const int output_depth = MatchingDim(filter_shape, 0, output_shape, 3); + if (bias_data) + { + assert(bias_shape.FlatSize() == output_depth); + UNUSED_RELEASE(bias_shape); + } + const int input_height = input_shape.Dims(1); + const int input_width = input_shape.Dims(2); + const int filter_height = filter_shape.Dims(1); + const int filter_width = filter_shape.Dims(2); + const int filter_input_depth = filter_shape.Dims(3); + const int groups = input_depth / filter_input_depth; + assert(input_depth % filter_input_depth == 0); + const int filters_per_group = output_depth / groups; + const int output_height = output_shape.Dims(1); + const int output_width = output_shape.Dims(2); + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int out_channel = 0; out_channel < output_depth; ++out_channel) + { + auto group = out_channel / filters_per_group; + const int in_x_origin = (out_x * stride_width) - pad_width; + const int in_y_origin = (out_y * stride_height) - pad_height; + int32_t acc = 0; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + for (int in_channel = 0; in_channel < filter_input_depth; ++in_channel) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + const int in_y = in_y_origin + dilation_height_factor * filter_y; + // If the location is outside the bounds of the input image, + // use zero as a default value. + if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height)) + { + int32_t input_val = input_data[Offset(input_shape, batch, in_y, in_x, + in_channel + group * filter_input_depth)]; + int32_t filter_val = + filter_data[Offset(filter_shape, out_channel, filter_y, filter_x, in_channel)]; + acc += filter_val * (input_val - input_offset[batch]); + } + } + } + } + float acc_float = acc * per_channel_scale[out_channel] * scaling_factors_ptr[batch]; + if (bias_data) + { + acc_float += bias_data[out_channel]; + } + output_data[Offset(output_shape, batch, out_y, out_x, out_channel)] = + ActivationFunctionWithMinMax(acc_float, output_activation_min, output_activation_max); + } + } + } + } +} + } // namespace reference } // namespace cker } // namespace nnfw diff --git a/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvHybrid.h b/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvHybrid.h new file mode 100644 index 0000000..9fc58ad --- /dev/null +++ b/compute/cker/include/cker/operation/reference/integer_ops/DepthwiseConvHybrid.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_HYBRID_H__ +#define __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_HYBRID_H__ + +#include "cker/Shape.h" +#include "cker/Types.h" +#include "cker/Utils.h" + +namespace nnfw +{ +namespace cker +{ +namespace reference_integer_ops +{ + +inline void DepthwiseConvHybridPerChannel(const DepthwiseConvParams ¶ms, + float *scaling_factors_ptr, const Shape &input_shape, + const int8_t *input_data, const Shape &filter_shape, + const int8_t *filter_data, const Shape &bias_shape, + const float *bias_data, const Shape &output_shape, + float *output_data, const float *per_channel_scale, + int32_t *input_offset) +{ + const int stride_width = params.stride_width; + const int stride_height = params.stride_height; + const int dilation_width_factor = params.dilation_width_factor; + const int dilation_height_factor = params.dilation_height_factor; + const int pad_width = params.padding_values.width; + const int pad_height = params.padding_values.height; + const int depth_multiplier = params.depth_multiplier; + const float output_activation_min = params.float_activation_min; + const float output_activation_max = params.float_activation_max; + + // Check dimensions of the tensors. + assert(input_shape.DimensionsCount() == 4); + assert(filter_shape.DimensionsCount() == 4); + assert(output_shape.DimensionsCount() == 4); + + const int batches = MatchingDim(input_shape, 0, output_shape, 0); + const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3); + const int input_height = input_shape.Dims(1); + const int input_width = input_shape.Dims(2); + const int input_depth = input_shape.Dims(3); + const int filter_height = filter_shape.Dims(1); + const int filter_width = filter_shape.Dims(2); + const int output_height = output_shape.Dims(1); + const int output_width = output_shape.Dims(2); + const int bias_depth = bias_shape.FlatSize(); + UNUSED_RELEASE(output_depth); + UNUSED_RELEASE(bias_shape); + assert(output_depth == input_depth * depth_multiplier); + assert(bias_depth == output_depth); + + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int in_channel = 0; in_channel < input_depth; ++in_channel) + { + for (int m = 0; m < depth_multiplier; ++m) + { + const int output_channel = m + in_channel * depth_multiplier; + const int in_x_origin = (out_x * stride_width) - pad_width; + const int in_y_origin = (out_y * stride_height) - pad_height; + int32_t acc = 0; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + const int in_y = in_y_origin + dilation_height_factor * filter_y; + // Zero padding by omitting the areas outside the image. + const bool is_point_inside_image = + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + if (is_point_inside_image) + { + int32_t input_val = + input_data[Offset(input_shape, batch, in_y, in_x, in_channel)]; + int32_t filter_val = + filter_data[Offset(filter_shape, 0, filter_y, filter_x, output_channel)]; + acc += filter_val * (input_val - input_offset[batch]); + } + } + } + float acc_float = static_cast(acc); + acc_float *= per_channel_scale[output_channel] * scaling_factors_ptr[batch]; + if (bias_data && output_channel < bias_depth) + { + acc_float += bias_data[output_channel]; + } + output_data[Offset(output_shape, batch, out_y, out_x, output_channel)] = + ActivationFunctionWithMinMax(acc_float, output_activation_min, output_activation_max); + } + } + } + } + } +} + +} // namespace reference_integer_ops +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_REFERENCE_DEPTHWISE_CONV_HYBRID_H__ diff --git a/compute/cker/include/cker/train/operation/FullyConnected.h b/compute/cker/include/cker/train/operation/FullyConnected.h new file mode 100644 index 0000000..b0255d2 --- /dev/null +++ b/compute/cker/include/cker/train/operation/FullyConnected.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_TRAIN_OPERATION_FULLY_CONNECTED_H__ +#define __NNFW_CKER_TRAIN_OPERATION_FULLY_CONNECTED_H__ + +#include "cker/eigen/Utils.h" +#include "cker/Shape.h" + +namespace nnfw +{ +namespace cker +{ +namespace train +{ + +template +inline void FullyConnectedBiasGrad(const Shape &incomming_shape, const T *incomming_data, + const Shape &grad_shape, T *grad_data) +{ + const auto bias_size = grad_shape.FlatSize(); + if (bias_size != incomming_shape.Dims(incomming_shape.DimensionsCount() - 1) || + bias_size != grad_shape.Dims(0)) + throw std::runtime_error("cker::FullyConnectedBiasGrad: Unmatched shape"); + + const auto in_mat = MapAsMatrixWithLastDimAsRows(incomming_data, incomming_shape); + auto grad_mat = MapAsMatrixWithLastDimAsRows(grad_data, grad_shape); + + grad_mat = in_mat.rowwise().sum(); +} + +} // namespace train +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_FULLY_CONNECTED_H__ diff --git a/compute/cker/include/cker/train/operation/Loss.h b/compute/cker/include/cker/train/operation/Loss.h new file mode 100644 index 0000000..94f49ff --- /dev/null +++ b/compute/cker/include/cker/train/operation/Loss.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_TRAIN_OPERATION_LOSS_H__ +#define __NNFW_CKER_TRAIN_OPERATION_LOSS_H__ + +#include "cker/Shape.h" +#include "cker/eigen/Utils.h" + +namespace nnfw +{ +namespace cker +{ +namespace train +{ + +template +inline void MSE(const Shape &y_pred_shape, const T *y_pred_data, const Shape &y_true_shape, + const T *y_true_data, const Shape &output_shape, T *output_data) +{ + // TODO Consider Reduction + if (output_shape != Shape{1}) + throw std::runtime_error("cker::MSE: output_shape != Shape{1}"); + if (y_pred_shape != y_true_shape) + throw std::runtime_error("cker::MSE: y_pred_shape != y_true_shape"); + + const auto y_pred = MapAsMatrixWithLastDimAsRows(y_pred_data, y_pred_shape); + const auto y_true = MapAsMatrixWithLastDimAsRows(y_true_data, y_true_shape); + + double squared_sum = 0.0f; + for (size_t c = 0; c < (size_t)y_pred.cols(); ++c) + { + for (size_t r = 0; r < (size_t)y_pred.rows(); ++r) + { + double error = y_pred.coeff(r, c) - y_true.coeff(r, c); + squared_sum += (error * error); + } + } + + auto size = y_pred.cols() * y_pred.rows(); + output_data[0] = (T)(squared_sum / size); +} + +template +inline void MSEGrad(const Shape &y_pred_shape, const T *y_pred_data, const Shape &y_true_shape, + const T *y_true_data, const Shape &grad_shape, T *grad_data) +{ + if (y_pred_shape != y_true_shape) + throw std::runtime_error("cker::MSEGrad: y_pred_shape != y_true_shape"); + if (y_pred_shape != grad_shape) + throw std::runtime_error("cker::MSEGrad: y_pred_shape != grad_shape"); + + const int size = grad_shape.FlatSize(); + for (int i = 0; i < size; ++i) + { + grad_data[i] = static_cast(-2 * (y_true_data[i] - y_pred_data[i]) / size); + } +} + +} // namespace train +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_TRAIN_OPERATION_LOSS_H__ diff --git a/compute/cker/include/cker/train/operation/ReLU.h b/compute/cker/include/cker/train/operation/ReLU.h new file mode 100644 index 0000000..32cf7fa --- /dev/null +++ b/compute/cker/include/cker/train/operation/ReLU.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NNFW_CKER_TRAIN_OPERATION_RELU_H__ +#define __NNFW_CKER_TRAIN_OPERATION_RELU_H__ + +#include "cker/Shape.h" +#include "cker/eigen/Utils.h" + +#include + +namespace nnfw +{ +namespace cker +{ +namespace train +{ + +inline void ReLUGrad(const Shape &output_shape, const float *output_data, + const Shape &incoming_shape, const float *incoming_data, + const Shape &grad_shape, float *grad_data) +{ + const auto output_map = MapAsVector(output_data, output_shape); + const auto incoming_map = MapAsVector(incoming_data, incoming_shape); + auto grad_map = MapAsVector(grad_data, grad_shape); + + if (output_shape == incoming_shape && output_shape == grad_shape) + grad_map.array() = incoming_map.array() * (output_map.array() > 0.0f).template cast(); + else + throw std::runtime_error("cker::ReLUGrad: Unsupported shape"); +} + +} // namespace train +} // namespace cker +} // namespace nnfw + +#endif // __NNFW_CKER_TRAIN_OPERATION_RELU_H__ diff --git a/compute/cker/src/train/FullyConnected.test.cc b/compute/cker/src/train/FullyConnected.test.cc new file mode 100644 index 0000000..37c2d4a --- /dev/null +++ b/compute/cker/src/train/FullyConnected.test.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +TEST(CKer_Operation, FullyConnectedBiasGrad) +{ + { + // Shape: {2, 4} + std::vector incoming_backward = {-1, 2, -3, 4, 5, -6, -7, 8}; + // Shape: {4} + std::vector expected_bias_backward = {4, -4, -10, 12}; + std::vector bias_backward(4); + + nnfw::cker::train::FullyConnectedBiasGrad( + nnfw::cker::Shape{2, 4}, incoming_backward.data(), + nnfw::cker::Shape{static_cast(bias_backward.size())}, bias_backward.data()); + + for (size_t i = 0; i < bias_backward.size(); ++i) + ASSERT_EQ(bias_backward[i], expected_bias_backward[i]); + } + + { + // Shape: {3, 3} + std::vector incoming_backward = {-1, 2, -3, 4, 5, -6, -7, 8, 9}; + // Shape: {3} + std::vector expected_bias_backward = {-4, 15, 0}; + std::vector bias_backward(3); + + nnfw::cker::train::FullyConnectedBiasGrad( + nnfw::cker::Shape{3, 3}, incoming_backward.data(), + nnfw::cker::Shape{static_cast(bias_backward.size())}, bias_backward.data()); + + for (size_t i = 0; i < bias_backward.size(); ++i) + ASSERT_EQ(bias_backward[i], expected_bias_backward[i]); + } + + { + // Shape: {1, 2, 2, 3} + std::vector incoming_backward = {-1, 2, -3, 4, 5, -6, -7, 8, 9, -10, -11, 12}; + // Shape: {3} + std::vector expected_bias_backward = {-14, 4, 12}; + std::vector bias_backward(3); + + nnfw::cker::train::FullyConnectedBiasGrad( + nnfw::cker::Shape{1, 2, 2, 3}, incoming_backward.data(), + nnfw::cker::Shape{static_cast(bias_backward.size())}, bias_backward.data()); + + for (size_t i = 0; i < bias_backward.size(); ++i) + ASSERT_EQ(bias_backward[i], expected_bias_backward[i]); + } +} + +TEST(CKer_Operation, neg_FullyConnectedBiasGrad) +{ + { + // Unmatched shape + // Shape: {2, 4} + std::vector incoming_backward = {-1, 2, -3, 4, 5, -6, -7, 8}; + // Shape: {3} + std::vector bias_backward(3); + EXPECT_ANY_THROW(nnfw::cker::train::FullyConnectedBiasGrad( + nnfw::cker::Shape{2, 4}, incoming_backward.data(), + nnfw::cker::Shape{static_cast(bias_backward.size())}, + bias_backward.data());); + } +} diff --git a/compute/cker/src/train/Loss.test.cc b/compute/cker/src/train/Loss.test.cc new file mode 100644 index 0000000..98568f4 --- /dev/null +++ b/compute/cker/src/train/Loss.test.cc @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +TEST(CKer_Operation, LossMSE) +{ + { + // Shape: {1, 10} -> m_rows:10, m_cols:1 + std::vector y_pred = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector y_true = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector output(1); + std::vector expected = {1}; + + nnfw::cker::train::MSE(nnfw::cker::Shape{1, 10}, y_pred.data(), nnfw::cker::Shape{1, 10}, + y_true.data(), nnfw::cker::Shape{1}, output.data()); + + EXPECT_EQ(output[0], expected[0]); + } + + { + // Shape: {1, 10} -> m_rows:10, m_cols:1 + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + std::vector output(1); + std::vector expected = {1.0}; + + nnfw::cker::train::MSE(nnfw::cker::Shape{1, 10}, y_pred.data(), nnfw::cker::Shape{1, 10}, + y_true.data(), nnfw::cker::Shape{1}, output.data()); + + EXPECT_FLOAT_EQ(output[0], expected[0]); + } + + { + // Shape: {2, 3} -> m_rows:3, m_cols:2 + std::vector y_pred = {27.2, 31.8, 51.9, 10.2, 34.2, 12.4}; + std::vector y_true = {31.3, 40.3, 29.7, 12.9, 25.8, 11.9}; + std::vector output(1); + std::vector expected = {110.0}; + + nnfw::cker::train::MSE(nnfw::cker::Shape{2, 3}, y_pred.data(), nnfw::cker::Shape{2, 3}, + y_true.data(), nnfw::cker::Shape{1}, output.data()); + + EXPECT_FLOAT_EQ(output[0], expected[0]); + } + + { + // Shape: {2, 3, 4} -> m_rows:4, m_cols:6 + std::vector y_pred = {1., 2., 3., 4., 1., 2., 3., 4., 1., 2., 3., 4., + 1., 2., 3., 4., 1., 2., 3., 4., 1., 2., 3., 4.}; + std::vector y_true = {1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3., + 1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.}; + std::vector output(1); + std::vector expected = {2.1666667}; + + nnfw::cker::train::MSE(nnfw::cker::Shape{2, 3, 4}, y_pred.data(), nnfw::cker::Shape{2, 3, 4}, + y_true.data(), nnfw::cker::Shape{1}, output.data()); + + EXPECT_FLOAT_EQ(output[0], expected[0]); + } +} + +TEST(CKer_Operation, neg_LossMSE) +{ + { + // Invalid expected value + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + std::vector output(1); + std::vector expected = {-1.0}; + + nnfw::cker::train::MSE(nnfw::cker::Shape{2, 3, 4}, y_pred.data(), nnfw::cker::Shape{2, 3, 4}, + y_true.data(), nnfw::cker::Shape{1}, output.data()); + + EXPECT_NE(output[0], expected[0]); + } + + { + // Invalid output shape + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + std::vector output(3); + std::vector expected = {1.0}; + + EXPECT_ANY_THROW(nnfw::cker::train::MSE(nnfw::cker::Shape{2, 3, 4}, y_pred.data(), + nnfw::cker::Shape{2, 3, 4}, y_true.data(), + nnfw::cker::Shape{3}, output.data())); + } + + { + // Different y_pread and y_true shape + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5.}; + std::vector output(1); + std::vector expected = {1.0}; + + EXPECT_ANY_THROW(nnfw::cker::train::MSE(nnfw::cker::Shape{2, 3, 4}, y_pred.data(), + nnfw::cker::Shape{2, 3}, y_true.data(), + nnfw::cker::Shape{1}, output.data())); + } +} + +TEST(CKer_Operation, LossMSEGrad) +{ + { + // Shape: {1, 10} -> m_rows:10, m_cols:1 + std::vector y_pred = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + std::vector y_true = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector deriv_y_pred(10); + std::vector expected = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + nnfw::cker::train::MSEGrad(nnfw::cker::Shape{1, 10}, y_pred.data(), nnfw::cker::Shape{1, 10}, + y_true.data(), nnfw::cker::Shape{1, 10}, deriv_y_pred.data()); + + for (size_t i = 0; i < deriv_y_pred.size(); ++i) + EXPECT_EQ(deriv_y_pred[i], expected[i]); + } + + { + // Shape: {1, 10} -> m_rows:10, m_cols:1 + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + std::vector deriv_y_pred(10); + std::vector expected = {0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2}; + + nnfw::cker::train::MSEGrad(nnfw::cker::Shape{1, 10}, y_pred.data(), nnfw::cker::Shape{1, 10}, + y_true.data(), nnfw::cker::Shape{1, 10}, deriv_y_pred.data()); + + for (size_t i = 0; i < deriv_y_pred.size(); ++i) + EXPECT_FLOAT_EQ(deriv_y_pred[i], expected[i]); + } + + { + // Shape: {2, 3} -> m_rows:3, m_cols:2 + std::vector y_pred = {27.2, 31.8, 51.9, 10.2, 34.2, 12.4}; + std::vector y_true = {31.3, 40.3, 29.7, 12.9, 25.8, 11.9}; + std::vector deriv_y_pred(6); + std::vector expected = {-1.3666667, -2.8333333, 7.4, -0.9, 2.8, 0.1666667}; + + nnfw::cker::train::MSEGrad(nnfw::cker::Shape{2, 3}, y_pred.data(), nnfw::cker::Shape{2, 3}, + y_true.data(), nnfw::cker::Shape{2, 3}, deriv_y_pred.data()); + + for (size_t i = 0; i < deriv_y_pred.size(); ++i) + EXPECT_FLOAT_EQ(deriv_y_pred[i], expected[i]); + } +} + +TEST(CKer_Operation, neg_LossMSEGrad) +{ + { + // Invalid expected value + std::vector y_pred = {27.2, 31.8, 51.9, 10.2, 34.2, 12.4}; + std::vector y_true = {31.3, 40.3, 29.7, 12.9, 25.8, 11.9}; + std::vector deriv_y_pred(6); + std::vector expected = {1., 1., 1., 1., 1., 1.}; + + nnfw::cker::train::MSEGrad(nnfw::cker::Shape{2, 3}, y_pred.data(), nnfw::cker::Shape{2, 3}, + y_true.data(), nnfw::cker::Shape{2, 3}, deriv_y_pred.data()); + + for (size_t i = 0; i < deriv_y_pred.size(); ++i) + EXPECT_NE(deriv_y_pred[i], expected[i]); + } + + { + // Different y_pred and y_true shape + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5.}; + std::vector deriv_y_pred(10); + + EXPECT_ANY_THROW(nnfw::cker::train::MSEGrad(nnfw::cker::Shape{1, 10}, y_pred.data(), + nnfw::cker::Shape{2, 3}, y_true.data(), + nnfw::cker::Shape{1, 10}, deriv_y_pred.data())); + } + + { + // Different y_pred and deriv_y_pred shape + std::vector y_pred = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; + std::vector y_true = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + std::vector deriv_y_pred(6); + + EXPECT_ANY_THROW(nnfw::cker::train::MSEGrad(nnfw::cker::Shape{1, 10}, y_pred.data(), + nnfw::cker::Shape{1, 10}, y_true.data(), + nnfw::cker::Shape{2, 3}, deriv_y_pred.data())); + } +} diff --git a/compute/cker/src/train/Relu.test.cc b/compute/cker/src/train/Relu.test.cc new file mode 100644 index 0000000..d944110 --- /dev/null +++ b/compute/cker/src/train/Relu.test.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +namespace +{ + +template class ReluOpVerifier +{ +public: + ReluOpVerifier(const std::vector &input, const std::vector &expected_output, + const std::vector &backprop_output, + const std::vector &expected_backprop_input) + : _input{input}, _expected_output{expected_output}, _backprop_output{backprop_output}, + _expected_backprop_input{expected_backprop_input} + { + EXPECT_TRUE(input.size() == expected_output.size()); + _output.resize(_expected_output.size()); + _backprop_input.resize(_expected_backprop_input.size()); + } + +public: + void verifyExpected() + { + nnfw::cker::ReLU(nnfw::cker::Shape{static_cast(_input.size())}, _input.data(), + nnfw::cker::Shape{static_cast(_output.size())}, _output.data()); + + for (size_t i = 0; i < _output.size(); ++i) + ASSERT_EQ(_output[i], _expected_output[i]); + + if (_backprop_output.size() > 0) + { + nnfw::cker::train::ReLUGrad( + nnfw::cker::Shape{static_cast(_output.size())}, _output.data(), + nnfw::cker::Shape{static_cast(_backprop_output.size())}, _backprop_output.data(), + nnfw::cker::Shape{static_cast(_backprop_input.size())}, _backprop_input.data()); + + for (size_t i = 0; i < _backprop_input.size(); ++i) + ASSERT_EQ(_backprop_input[i], _expected_backprop_input[i]); + } + } + +private: + std::vector _input; + std::vector _output; + std::vector _expected_output; + std::vector _backprop_output; + std::vector _backprop_input; + std::vector _expected_backprop_input; +}; + +} // namespace + +TEST(CKer_Operation, ReLU) +{ + { + std::vector input_forward = {-1, 2, 3, -4}; + std::vector expected_forward = {0, 2, 3, 0}; + std::vector incoming_backward = {-5, 6, -7, 8}; + std::vector expected_backward = {0, 6, -7, 0}; + ReluOpVerifier verifier{input_forward, expected_forward, incoming_backward, + expected_backward}; + verifier.verifyExpected(); + } + + { + std::vector input_forward = {0, -1, 2, 3, -4, 5, 6, -7}; + std::vector expected_forward = {0, 0, 2, 3, 0, 5, 6, 0}; + std::vector incoming_backward = {8, -9, 10, 11, -12, -13, 14, -15}; + std::vector expected_backward = {0, 0, 10, 11, 0, -13, 14, 0}; + ReluOpVerifier verifier{input_forward, expected_forward, incoming_backward, + expected_backward}; + verifier.verifyExpected(); + } +} + +TEST(CKer_Operation, neg_ReLU) +{ + { + // Unmatched shape + std::vector input_forward = {0, -1, 2, 3, -4}; + std::vector expected_forward = {0, 0, 2, 3, 0}; + std::vector incoming_backward = {-5, 6, -7, 8}; + std::vector expected_backward = {0, 6, -7, 0}; + ReluOpVerifier verifier{input_forward, expected_forward, incoming_backward, + expected_backward}; + EXPECT_ANY_THROW(verifier.verifyExpected()); + } +} diff --git a/compute/ruy/include/ruy/Shape.h b/compute/ruy/include/ruy/Shape.h index 981c5b4..151a673 100644 --- a/compute/ruy/include/ruy/Shape.h +++ b/compute/ruy/include/ruy/Shape.h @@ -156,7 +156,7 @@ public: const int dimensions_count = std::distance(src_iterable.begin(), src_iterable.end()); Resize(dimensions_count); int32_t *data = DimsData(); - for (auto it : src_iterable) + for (auto &&it : src_iterable) { *data = it; ++data; diff --git a/docs/conf.py b/docs/conf.py index c33e103..5025671 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ copyright = '2020, Samsung Research & contributors' author = 'Samsung Research & contributors' # The full version, including alpha/beta/rc tags -release = '1.22.1' +release = '1.25.0' # -- General configuration --------------------------------------------------- diff --git a/docs/howto/how-to-build-compiler.md b/docs/howto/how-to-build-compiler.md index 7a1fa1c..3a96386 100644 --- a/docs/howto/how-to-build-compiler.md +++ b/docs/howto/how-to-build-compiler.md @@ -45,9 +45,6 @@ pylint \ python3 \ python3-pip \ python3-venv \ -python3.8 \ -python3.8-dev \ -python3.8-venv \ scons \ software-properties-common \ unzip \ @@ -62,6 +59,16 @@ $ sudo mv *.a /usr/lib $ pip install yapf==0.22.0 numpy ``` +Additional install python3.8 if you are using Ubuntu 18.04. +``` +$ sudo apt-get install \ +python3.8 \ +python3.8-dev \ +python3.8-venv +``` + +If you get `Unable to locate package clang-format-8` then just use `clang-format`. + ## Build for Ubuntu In a typical linux development environment, including Ubuntu, you can build the compiler with a diff --git a/docs/howto/how-to-build-runtime.md b/docs/howto/how-to-build-runtime.md index f83ec5b..f4d7b56 100644 --- a/docs/howto/how-to-build-runtime.md +++ b/docs/howto/how-to-build-runtime.md @@ -1,11 +1,12 @@ # How to Build Runtime -This document is based on the system where Ubuntu Desktop Linux 18.04 LTS is installed with default settings, and can be applied in other environments without much difference. For reference, the development of our project started in the Ubuntu Desktop Linux 16.04 LTS environment. +This document is based on the system where Ubuntu Desktop Linux 20.04 LTS is installed with default settings, and can be applied in other environments without much difference. For reference, the development of our project started in the Ubuntu Desktop Linux 16.04 LTS environment. ## Build requirements If you are going to build this project, the following modules must be installed on your system: +- C & C++ compiler - CMake - Boost C++ libraries @@ -15,9 +16,9 @@ In the Ubuntu, you can easily install it with the following command. $ sudo apt-get install cmake libboost-all-dev ``` -If your linux system does not have the basic development configuration, you will need to install more packages. A list of all packages needed to configure the development environment can be found in https://github.com/Samsung/ONE/blob/master/infra/docker/bionic/Dockerfile. +If your linux system does not have the basic development configuration, you will need to install more packages. A list of all packages needed to configure the development environment can be found in https://github.com/Samsung/ONE/blob/master/infra/docker/focal/Dockerfile. -Here is a summary of it +Here is a summary of it for runtime and related tools ``` $ sudo apt install \ @@ -29,30 +30,16 @@ git \ graphviz \ hdf5-tools \ lcov \ -libatlas-base-dev \ libboost-all-dev \ -libgflags-dev \ -libgoogle-glog-dev \ -libgtest-dev \ libhdf5-dev \ -libprotobuf-dev \ -protobuf-compiler \ -pylint \ python3 \ python3-pip \ -python3-venv \ scons \ software-properties-common \ unzip \ wget -$ mkdir /tmp/gtest -$ cd /tmp/gtest -$ cmake /usr/src/gtest -$ make -$ sudo mv *.a /usr/lib - -$ pip install yapf==0.22.0 numpy +$ pip3 install yapf==0.22.0 numpy ``` @@ -63,10 +50,18 @@ In a typical linux development environment, including Ubuntu, you can build the ``` $ git clone https://github.com/Samsung/ONE.git one $ cd one -$ make -f Makefile.template install +$ ./nnfw configure +$ ./nnfw build +$ ./nnfw install +``` + +For easy build process, we provides `Makefile.template` makefile. + +``` +$ make -f Makefile.template ``` -Unfortunately, the debug build on the x86_64 architecture currently has an error. To solve the problem, you must use gcc version 9 or higher. Another workaround is to do a release build rather than a debug build. This is not a suitable method for debugging during development, but it is enough to check the function of the runtime. To release build the runtime, add the environment variable `BUILD_TYPE=release` to the build command as follows. +To release build the runtime, add the environment variable `BUILD_TYPE=release` to the build command as follows. ``` $ export BUILD_TYPE=release @@ -94,77 +89,74 @@ $ tree -L 2 ./Product $ tree -L 3 ./Product/out ./Product/out ├── bin -│   ├── onert_run -│   ├── tflite_comparator -│   └── tflite_run +│ ├── onert_run +│ ├── tflite_comparator +│ └── tflite_run ├── include -│   ├── nnfw -│   │   ├── NeuralNetworksEx.h -│   │   ├── NeuralNetworksExtensions.h -│   │   ├── NeuralNetworks.h -│   │   ├── nnfw_experimental.h -│   │   └── nnfw.h -│   └── onert -│   ├── backend -│   ├── compiler -│   ├── exec -│   ├── ir -│   └── util +│ ├── nnfw +│ │ ├── NeuralNetworksEx.h +│ │ ├── NeuralNetworksExtensions.h +│ │ ├── NeuralNetworks.h +│ │ ├── nnfw_experimental.h +│ │ └── nnfw.h +│ └── onert +│ ├── backend +│ ├── compiler +│ ├── exec +│ ├── ir +│ └── util ├── lib -│   ├── libbackend_cpu.so -│   ├── libbackend_ruy.so -│   ├── libneuralnetworks.so -│   ├── libnnfw-dev.so -│   └── libonert_core.so +│ ├── libbackend_cpu.so +│ ├── libbackend_ruy.so +│ ├── libneuralnetworks.so +│ ├── libnnfw-dev.so +│ └── libonert_core.so ├── nnapi-gtest -│   ├── nnapi_gtest -│   ├── nnapi_gtest.skip -│   ├── nnapi_gtest.skip.noarch.interp -│   └── nnapi_gtest.skip.x86_64-linux.cpu +│ ├── nnapi_gtest +│ ├── nnapi_gtest.skip +│ └── nnapi_gtest.skip.x86_64-linux.cpu ├── test -│   ├── command -│   │   ├── nnpkg-test -│   │   ├── prepare-model -│   │   ├── unittest -│   │   └── verify-tflite -│   ├── FillFrom_runner -│   ├── list -│   │   ├── benchmark_nnpkg_model_list.txt -│   │   ├── nnpkg_test_list.armv7l-linux.acl_cl -│   │   ├── nnpkg_test_list.armv7l-linux.acl_neon -│   │   ├── nnpkg_test_list.armv7l-linux.cpu -│   │   ├── nnpkg_test_list.noarch.interp -│   │   ├── tflite_comparator.aarch64.acl_cl.list -│   │   ├── tflite_comparator.aarch64.acl_neon.list -│   │   ├── tflite_comparator.aarch64.cpu.list -│   │   ├── tflite_comparator.armv7l.acl_cl.list -│   │   ├── tflite_comparator.armv7l.acl_neon.list -│   │   ├── tflite_comparator.armv7l.cpu.list -│   │   ├── tflite_comparator.noarch.interp.list -│   │   └── tflite_comparator.x86_64.cpu.list -│   ├── models -│   │   ├── run_test.sh -│   │   └── tflite -│   ├── nnpkgs -│   │   └── FillFrom -│   └── onert-test +│ ├── command +│ │ ├── nnpkg-test +│ │ ├── prepare-model +│ │ ├── unittest +│ │ └── verify-tflite +│ ├── FillFrom_runner +│ ├── list +│ │ ├── benchmark_nnpkg_model_list.txt +│ │ ├── nnpkg_test_list.armv7l-linux.acl_cl +│ │ ├── nnpkg_test_list.armv7l-linux.acl_neon +│ │ ├── nnpkg_test_list.armv7l-linux.cpu +│ │ ├── tflite_comparator.aarch64.acl_cl.list +│ │ ├── tflite_comparator.aarch64.acl_neon.list +│ │ ├── tflite_comparator.aarch64.cpu.list +│ │ ├── tflite_comparator.armv7l.acl_cl.list +│ │ ├── tflite_comparator.armv7l.acl_neon.list +│ │ ├── tflite_comparator.armv7l.cpu.list +│ │ └── tflite_comparator.x86_64.cpu.list +│ ├── models +│ │ ├── run_test.sh +│ │ └── tflite +│ ├── nnpkgs +│ │ └── FillFrom +│ └── onert-test └── unittest ├── ndarray_test ├── nnfw_api_gtest ├── nnfw_api_gtest_models - │   ├── add - │   ├── add_invalid_manifest - │   ├── add_no_manifest - │   ├── if_dynamic - │   ├── mobilenet_v1_1.0_224 - │   └── while_dynamic + │ ├── add + │ ├── add_invalid_manifest + │ ├── add_no_manifest + │ ├── if_dynamic + │ ├── mobilenet_v1_1.0_224 + │ └── while_dynamic ├── nnfw_lib_misc_test ├── test_cker ├── test_onert_core ├── test_onert_frontend_nnapi └── tflite_test -26 directories, 46 files +26 directories, 42 files ``` @@ -184,7 +176,7 @@ inception_v3.tflite The result of running the inception_v3 model using runtime is as follows. Please consider that this is a test that simply checks execution latency without considering the accuracy of the model. ``` -$ ./Product/out/bin/onert_run --modelfile ./inception_v3.tflite +$ ./Product/out/bin/onert_run ./inception_v3.tflite Model Filename ./inception_v3.tflite =================================== MODEL_LOAD takes 1.108 ms diff --git a/docs/howto/how-to-cross-build-runtime-for-arm.md b/docs/howto/how-to-cross-build-runtime-for-arm.md index 91481a9..2db4d56 100644 --- a/docs/howto/how-to-cross-build-runtime-for-arm.md +++ b/docs/howto/how-to-cross-build-runtime-for-arm.md @@ -13,26 +13,26 @@ Use `install_rootfs.sh` script to prepare Root File System. You should have `sud ``` $ sudo ./tools/cross/install_rootfs.sh arm ``` -- supports `arm`(default) and `aarch` architecutre for now -- supports `bionic`(default), and `focal` release +- supports `arm`(default) and `aarch64` architecutre for now +- supports `focal`(default), `bionic`, and `jammy` release To see the options, ``` $ ./tools/cross/install_rootfs.sh -h ``` -RootFS will be prepared at `tools/cross/rootfs/arm` folder. +RootFS will be prepared at `tools/cross/rootfs/arm` or `tools/cross/rootfs/aarch64` folder. ***\* CAUTION: The OS version of rootfs must match the OS version of execution target device. On the other hand, you need to match the Ubuntu version of the development PC with the Ubuntu version of rootfs to be used for cross-build. Otherwise, unexpected build errors may occur.*** -If you are using Ubuntu 18.04 LTS, select `bionic`, if you are using Ubuntu 20.04 LTS, select `focal`. You can check your Ubuntu code name in the following way. +If you are using Ubuntu 20.04 LTS, select `focal`, if you are using Ubuntu 22.04 LTS, select `jammy`. You can check your Ubuntu code name in the following way. ``` $ cat /etc/lsb-release DISTRIB_ID=Ubuntu -DISTRIB_RELEASE=18.04 -DISTRIB_CODENAME=bionic -DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS" +DISTRIB_RELEASE=22.04 +DISTRIB_CODENAME=jammy +DISTRIB_DESCRIPTION="Ubuntu 22.04.3 LTS" ``` If a build error occurs because the version of the development system and the target system do not match, and if you can't replace your development system for any reason, you can consider [cross-build using the docker image](how-to-build-runtime-using-prebuilt-docker-image.md). @@ -61,29 +61,32 @@ for `http`, `https` and `ftp` protocol. ## Install ARM Cross Toolchain -We recommend you have g++ >= 6 installed on your system because NN generated tests require it. +We recommend you have g++ >= 6.1 installed on your system because NN generated tests require it (c++14). -### Ubuntu 18.04 LTS +### Ubuntu 20.04 LTS -On Ubuntu 18.04 LTS, you can install using `apt-get`. +On Ubuntu 20.04 LTS, you can install using `apt-get`. -Choose g++ version whatever you prefer: 7 (default), 6 or 8. +Choose g++ version whatever you prefer: 9 (default) or 10. We are officially testing on default g++ version, +so we don't confirm build on different version. ``` -$ sudo apt-get install g++-{6,7,8}-arm-linux-gnueabihf +$ sudo apt-get install g++-{9,10}-arm-linux-gnueabihf ``` -If you select specific version, update symbolic link for build toolchain +If you select specific version, update symbolic link for build toolchain. + +Otherwise, you should set your custom cmake crossbuild toolchain. You can find cmake toolchain files in `infra/nnfw/cmake/buildtool/cross/`. ``` -$ update-alternatives --install /usr/bin/arm-linux-gnueabihf-gcc arm-linux-gnueabihf-gcc /usr/bin/arm-linux-gnueabihf-gcc-8 80 \ - --slave /usr/bin/arm-linux-gnueabihf-g++ arm-linux-gnueabihf-g++ /usr/bin/arm-linux-gnueabihf-g++-8 \ - --slave /usr/bin/arm-linux-gnueabihf-gcov arm-linux-gnueabihf-gcov /usr/bin/arm-linux-gnueabihf-gcov-8 +$ update-alternatives --install /usr/bin/arm-linux-gnueabihf-gcc arm-linux-gnueabihf-gcc /usr/bin/arm-linux-gnueabihf-gcc-10 80 \ + --slave /usr/bin/arm-linux-gnueabihf-g++ arm-linux-gnueabihf-g++ /usr/bin/arm-linux-gnueabihf-g++-10 \ + --slave /usr/bin/arm-linux-gnueabihf-gcov arm-linux-gnueabihf-gcov /usr/bin/arm-linux-gnueabihf-gcov-10 ``` -### Ubuntu 20.04 LTS +### Ubuntu 22.04 LTS -Same with Ubuntu 18.04 LTS. (except g++ version) +Same with Ubuntu 20.04 LTS. (except g++ version) ## Build and install ARM Compute Library @@ -95,18 +98,18 @@ To build ACL, you need to install scons $ sudo apt-get install scons ``` -ACL will be automatically installed in `externals/acl` when you build runtime without any changes. +ACL source will be automatically installed in `externals/ARMCOMPUTE` when you build runtime without any changes. You can check ACL source information in `infra/cmake/packages/ARMComputeSourceConfig.cmake` -## Cross build for ARM +## Cross build for ARM by using Makefile.template Give `TARGET_ARCH` variable to set the target architecture. If you used `ROOTFS_DIR` to prepare in alternative folder, you should also give this to makefile. ``` -$ CROSS_BUILD=1 TARGET_ARCH=armv7l make +$ CROSS_BUILD=1 TARGET_ARCH=armv7l make -f Makefile.template # If ROOTFS_DIR is in alternative folder $ ROOTFS_DIR=/path/to/your/rootfs/arm \ @@ -121,15 +124,23 @@ normal build and cross build as follows. ``` $ export ROOTFS_DIR=xxx ... -$ make # do normal build -$ TARGET_ARCH=armv7l make # do cross build +$ make -f Makefile.template # do normal build +$ TARGET_ARCH=armv7l make -f Makefile.template # do cross build ``` +Makefile.template will pass crossbuild toolchain setting to cmake automatically by parsing variables. + ### Run test -To run and test the cross-compiled runtime, you need to copy the compiled output to the target device of the architecture in which it is executable. +To run and test the cross-compiled runtime, you need to install library packages and copy the compiled output to the target device of the architecture in which it is executable. + +1. Install hdf5 and boost library package + +``` +$ sudo apt install libhdf5-dev libboost-system-dev libboost-program-options-dev +``` -1. Copy all artifacts under the `./Product` folder to the target device, Odroid-XU4 for example, as a whole. +2. Copy all artifacts under the `./Product/armv7l-linux.` folder to the target device, Odroid-XU4 for example, as a whole. ``` $ ssh odroid mkdir -p one/Product @@ -144,7 +155,7 @@ test-driver.sh ... ``` -2. Log in to the target device, go to the copied path, and reestore the symbolic link settings of the `Product` directory. +3. Log in to the target device, go to the copied path, and reestore the symbolic link settings of the `Product` directory. ``` $ ssh odroid diff --git a/docs/release/1.23/index.rst b/docs/release/1.23/index.rst new file mode 100644 index 0000000..ae29d52 --- /dev/null +++ b/docs/release/1.23/index.rst @@ -0,0 +1,13 @@ +.. ONE documentation master file, created by + sphinx-quickstart on Thu May 18 19:07:17 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +1.23 +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + ./release-note-1.23.0.md diff --git a/docs/release/1.23/release-note-1.23.0.md b/docs/release/1.23/release-note-1.23.0.md new file mode 100644 index 0000000..b5a3d1b --- /dev/null +++ b/docs/release/1.23/release-note-1.23.0.md @@ -0,0 +1,8 @@ +# Release Note 1.23.0 + +## ONE Compiler + +- Support more Op(s): GeLU +- Support more option(s): `--fuse-gelu` +- Support multiple backends compilation with a single configuration file +- Upgrade Circle schema to 0.5 diff --git a/docs/release/1.24/index.rst b/docs/release/1.24/index.rst new file mode 100644 index 0000000..fa16988 --- /dev/null +++ b/docs/release/1.24/index.rst @@ -0,0 +1,13 @@ +.. ONE documentation master file, created by + sphinx-quickstart on Thu Jul 18 14:08:15 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +1.24 +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + ./release-note-1.24.0.md diff --git a/docs/release/1.24/release-note-1.24.0.md b/docs/release/1.24/release-note-1.24.0.md new file mode 100644 index 0000000..9020da8 --- /dev/null +++ b/docs/release/1.24/release-note-1.24.0.md @@ -0,0 +1,9 @@ +# Release Note 1.24.0 + +## ONE Compiler + +- Introduce _one-import-onnx_ extension interface +- _onecc_ supports profiling of multiple backends with a single cfg file +- Enable more Quantize operator: FloorMod, Squeeze +- _visq_ supports multi-out nodes +- _onecc_ introduces `dynamic_batch_to_single_batch option` option. diff --git a/docs/release/1.25/index.rst b/docs/release/1.25/index.rst new file mode 100644 index 0000000..1965b42 --- /dev/null +++ b/docs/release/1.25/index.rst @@ -0,0 +1,13 @@ +.. ONE documentation master file, created by + sphinx-quickstart on Wed Sep 06 12:18:12 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +1.25 +==== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + ./release-note-1.25.0.md diff --git a/docs/release/1.25/release-note_1.25.0.md b/docs/release/1.25/release-note_1.25.0.md new file mode 100644 index 0000000..3d62c6b --- /dev/null +++ b/docs/release/1.25/release-note_1.25.0.md @@ -0,0 +1,17 @@ +# Release Note 1.25.0 + +## ONE Runtime + +- Support ubuntu 20.04 + +### CPU Backend Operation +- CPU backend supports per-channel hybrid quantization of int8 type weight and float activation. (TFLite's dynamic range quantization) + +### On-device Quantization +- _onert_ supports new experimental API for on-device quantization. +- As the 1st step, _onert_ supports per-channel hybrid quantization of int8/int16 type weight and float activation. +- API requires file path to export quantized model. + +### Minmax Recorder +- _onert_` support minmax recording of each layer as experimental feature. It is not supported by API yet. +- Output file format is HDF5. (File format may change later). diff --git a/docs/release/onert-micro/0.1/release-note-0.1.0.md b/docs/release/onert-micro/0.1/release-note-0.1.0.md new file mode 100644 index 0000000..5a4692b --- /dev/null +++ b/docs/release/onert-micro/0.1/release-note-0.1.0.md @@ -0,0 +1,72 @@ +## Release Notes for onert-micro 0.1.0 + +_onert-micro_ is tiny runtime specialized for running NN model in MCU boards. Note that onert-micro is under active development and is subject to change. + +### Supported operations + +For MCU board, we support 22 operations as follows : + + ADD, FULLY_CONNECTED, CONV_2D, LOGISTIC ,GATHER, EXPAND_DIMS, PACK, RESHAPE, REDUCE_PROD, LESS, MUL, MAX_POOL_2D, CONCATENATION, SHAPE, SLICE, SUB, SPLIT, STRIDED_SLICE, TANH, SOFTMAX, WHILE, UNIDIRECTIONAL_SEQUENCE_LSTM + +### RNN Model + +#### LSTM + +onert-micro supports Keras model with LSTM operations. But, it should be converted to UNIDIRECTIONAL_SEQUENCE_LSTM operation in circle format. + +#### GRU + +onert-micro supports model with GRU Operations, which is converted from Keras Model. Please refer to https://github.com/Samsung/ONE/issues/10465 to see GRU operation supported by onert-micro. + +### Benchmark + +onert-micro shows better performance than tflite-micro especially in memory consumption, binary size. + +The measurement is done on TizenRT running reference models on the development board with the following spec : + +- 32-bit Arm Cortex-M33 200MHz +- 4MB RAM, 8MB Flash + +Commit for measurement : +- tflite-micro commit: https://github.com/tensorflow/tflite-micro/commit/4e62ea7b821c1e6af004912132395fb81922ea8d + +- onert-micro commit: https://github.com/Samsung/ONE/commit/c763867500fe3d80bfd1ef834990d34a81640d17 +#### L model + +| Params | Tflite micro | Onert-micro | +|-----------------------------------|---------------|-------------| +| Execution time(us)* | **2 912 700** | 2 953 000 | +| RAM consumption(bytes) | 126 800 | **93 376** | +| Binary file size overhead (bytes) | 57 676 | **32 248** | + + +#### T1 model + +Params | Tflite micro | Onert-micro | +--- | --- | --- +Execution time(us)* | **1 340** | 1 510 | +RAM consumption(bytes) | 1 640 | **1 152** | +Binary file size overhead (bytes) | 35 040 | **19 432** | + +#### T2 model + +Params | Tflite micro** | Onert-micro | +--- | --- | --- +Execution time(us)* | N/A | 5 090 | +RAM consumption(bytes) | N/A | 3 360 | +Binary file size overhead (bytes) | N/A | 30 488 | + +#### Model with GRU operations + +- model link : https://github.com/Samsung/ONE/files/8368702/gru.zip + +Params | Tflite micro** | Onert-micro | +--- | --- | --- +Execution time(us)* | N/A | 335 000 | +RAM consumption(bytes) | N/A | 14 816 | +Binary file size overhead (bytes) | N/A | 43 444 | + + +(*) Average for 100 inferences +(**) Tflite-micro has not launched this model + diff --git a/docs/release/onert-micro/1.0/release-note-1.0.0.md b/docs/release/onert-micro/1.0/release-note-1.0.0.md new file mode 100644 index 0000000..a18d557 --- /dev/null +++ b/docs/release/onert-micro/1.0/release-note-1.0.0.md @@ -0,0 +1,12 @@ +## Release Notes for onert-micro 1.0 + +### Supported operations + +More operations are supported as follows: + +- AveragePool2D, Elu, Exp, Abs, Neg, Div, AddN, Relu, Relu6, Leak_Relu, Pad, PadV2, ArgMin, ArgMax, Resize_Bilinear, LogicalAnd, LogicalOr, Equal, NotEqual, Greater, GreaterEqual, LessEqual + +### Etc + +- Address sanitizer build option(ENABLE_SANITIZER) is added +- Fix buffer overflow in static analyzer's defect diff --git a/infra/cmake/packages/AbseilSourceConfig.cmake b/infra/cmake/packages/AbseilSourceConfig.cmake index 0297c08..8d0c779 100644 --- a/infra/cmake/packages/AbseilSourceConfig.cmake +++ b/infra/cmake/packages/AbseilSourceConfig.cmake @@ -7,13 +7,13 @@ function(_AbseilSource_import) nnas_include(ExternalSourceTools) nnas_include(OptionTools) - # NOTE TensorFlow 2.9 downloads abseil 20211102.0 + # NOTE GCC 13 requires abseil 20230125.3 envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") - envoption(ABSEIL_URL ${EXTERNAL_DOWNLOAD_SERVER}/abseil/abseil-cpp/archive/20211102.0.tar.gz) + envoption(ABSEIL_URL ${EXTERNAL_DOWNLOAD_SERVER}/abseil/abseil-cpp/archive/20230125.3.tar.gz) ExternalSource_Download(ABSEIL DIRNAME ABSEIL URL ${ABSEIL_URL} - CHECKSUM MD5=bdca561519192543378b7cade101ec43) + CHECKSUM MD5=9b6dae642c4bd92f007ab2c148bc0498) set(AbseilSource_DIR ${ABSEIL_SOURCE_DIR} PARENT_SCOPE) set(AbseilSource_FOUND TRUE PARENT_SCOPE) diff --git a/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfig.cmake b/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfig.cmake new file mode 100644 index 0000000..06106dc --- /dev/null +++ b/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfig.cmake @@ -0,0 +1,14 @@ +function(_CMSIS_NN_import) + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(CMSIS_NN_4_1_0_URL ${EXTERNAL_DOWNLOAD_SERVER}/ARM-software/CMSIS-NN/archive/refs/tags/v4.1.0.tar.gz) + + ExternalSource_Download(CMSIS_NN DIRNAME CMSIS-NN-4.1.0 ${CMSIS_NN_4_1_0_URL}) + + set(CMSIS_NNSource_DIR ${CMSIS_NN_SOURCE_DIR} PARENT_SCOPE) + set(CMSIS_NNSource_FOUND TRUE PARENT_SCOPE) +endfunction(_CMSIS_NN_import) + +_CMSIS_NN_import() diff --git a/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfigVersion.cmake b/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfigVersion.cmake new file mode 100644 index 0000000..5296e19 --- /dev/null +++ b/infra/cmake/packages/CMSIS-NN-4.1.0/CMSIS-NNConfigVersion.cmake @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION "4.1.0") +set(PACKAGE_VERSION_EXACT FALSE) +set(PACKAGE_VERSION_COMPATIBLE FALSE) +set(PACKAGE_VERSION_UNSUITABLE TRUE) + +if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + set(PACKAGE_VERSION_UNSUITABLE FALSE) +endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfig.cmake b/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfig.cmake new file mode 100644 index 0000000..81fc6ae --- /dev/null +++ b/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfig.cmake @@ -0,0 +1,19 @@ +function(_TensorFlowSource_import) + if(NOT DOWNLOAD_TENSORFLOW) + set(TensorFlowSource_FOUND FALSE PARENT_SCOPE) + return() + endif(NOT DOWNLOAD_TENSORFLOW) + + nnas_include(ExternalSourceTools) + nnas_include(OptionTools) + + envoption(EXTERNAL_DOWNLOAD_SERVER "https://github.com") + envoption(TENSORFLOW_2_12_1_URL ${EXTERNAL_DOWNLOAD_SERVER}/tensorflow/tensorflow/archive/v2.12.1.tar.gz) + + ExternalSource_Download(TENSORFLOW DIRNAME TENSORFLOW-2.12.1 ${TENSORFLOW_2_12_1_URL}) + + set(TensorFlowSource_DIR ${TENSORFLOW_SOURCE_DIR} PARENT_SCOPE) + set(TensorFlowSource_FOUND TRUE PARENT_SCOPE) +endfunction(_TensorFlowSource_import) + +_TensorFlowSource_import() diff --git a/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfigVersion.cmake b/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfigVersion.cmake new file mode 100644 index 0000000..8566d08 --- /dev/null +++ b/infra/cmake/packages/TensorFlowSource-2.12.1/TensorFlowSourceConfigVersion.cmake @@ -0,0 +1,10 @@ +set(PACKAGE_VERSION "2.12.1") +set(PACKAGE_VERSION_EXACT FALSE) +set(PACKAGE_VERSION_COMPATIBLE FALSE) +set(PACKAGE_VERSION_UNSUITABLE TRUE) + +if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + set(PACKAGE_VERSION_UNSUITABLE FALSE) +endif(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) diff --git a/infra/command/build-docker-image b/infra/command/build-docker-image index 9a04438..f4e2069 100644 --- a/infra/command/build-docker-image +++ b/infra/command/build-docker-image @@ -45,6 +45,12 @@ done DOCKER_IMAGE_NAME=${DOCKER_IMAGE_NAME:-nnfw/one-devtools:$DOCKER_TAG} DOCKER_FILE_RPATH=$DOCKER_FILE_RPATH_BASE/$UBUNTU_CODENAME/Dockerfile + +HOST_ARCH=$(uname -m) +if [[ -n $HOST_ARCH && $HOST_ARCH != "x86_64" ]]; then + DOCKER_FILE_RPATH=$DOCKER_FILE_RPATH.$HOST_ARCH +fi + DOCKER_BUILD_ARGS+=("-t ${DOCKER_IMAGE_NAME}") docker build --build-arg http_proxy="${http_proxy}" \ diff --git a/infra/debian/compiler/changelog b/infra/debian/compiler/changelog index ba5eea9..c4d3584 100644 --- a/infra/debian/compiler/changelog +++ b/infra/debian/compiler/changelog @@ -1,3 +1,22 @@ +one (1.24.0) bionic; urgency=medium + + * Introduce _one-import-onnx_ extension interface + * _onecc_ supports profiling of multiple backends with a single cfg file + * Enable more Quantize operator: FloorMod, Squeeze + * _visq_ supports multi-out nodes + * _onecc_ introduces `dynamic_batch_to_single_batch option` option. + + -- seongwoo Thu, 18 Jul 2023 14:10:22 +0900 + +one (1.23.0) bionic; urgency=medium + + * Support more Op(s): GeLU + * Support more option(s): `--fuse-gelu` + * Support multiple backends compilation with a single configuration file + * Upgrade Circle schema to 0.5 + + -- seongwoo Thu, 18 May 2023 19:10:21 +0900 + one (1.22.0) bionic; urgency=medium * Introduce new optimization options: `unroll_unidirseqlstm`, `forward_transpose_op`, `fold_fully_connected`, `fuse_prelu` diff --git a/infra/debian/compiler/one-compiler.install b/infra/debian/compiler/one-compiler.install index 641bbe6..700cc2d 100644 --- a/infra/debian/compiler/one-compiler.install +++ b/infra/debian/compiler/one-compiler.install @@ -3,6 +3,7 @@ usr/bin/circle2circle usr/share/one/bin/ usr/bin/circle-eval-diff usr/share/one/bin/ usr/bin/circle-interpreter usr/share/one/bin/ +usr/bin/circle-mpqsolver usr/share/one/bin/ usr/bin/circle-operator usr/share/one/bin/ usr/bin/circle-partitioner usr/share/one/bin/ usr/bin/circle-quantizer usr/share/one/bin/ @@ -28,6 +29,7 @@ usr/bin/one-prepare-venv usr/share/one/bin/ usr/bin/one-profile usr/share/one/bin/ usr/bin/one-quantize usr/share/one/bin/ usr/bin/one-version usr/share/one/bin/ +usr/bin/onelib/backends.py usr/share/one/bin/onelib/ usr/bin/onelib/constant.py usr/share/one/bin/onelib/ usr/bin/onelib/make_cmd.py usr/share/one/bin/onelib/ usr/bin/onelib/CfgRunner.py usr/share/one/bin/onelib/ @@ -49,6 +51,8 @@ usr/bin/visqlib/DumpFP32FM.py usr/share/one/bin/visqlib/ usr/bin/visqlib/Palette.py usr/share/one/bin/visqlib/ usr/bin/visqlib/QErrorComputer.py usr/share/one/bin/visqlib/ usr/bin/visqlib/Util.py usr/share/one/bin/visqlib/ +usr/bin/visqlib/DotBuilder.py usr/share/one/bin/visqlib/ +usr/bin/circle/*.py usr/share/one/bin/circle/ # lib usr/lib/* usr/share/one/lib/ # doc diff --git a/infra/debian/compiler/rules b/infra/debian/compiler/rules index 447fb21..e83680d 100755 --- a/infra/debian/compiler/rules +++ b/infra/debian/compiler/rules @@ -1,7 +1,7 @@ #!/usr/bin/make -f export DH_VERBOSE = 1 export NNAS_BUILD_PREFIX = build -export PRESET = 20221125 +export PRESET = 20230413 export _DESTDIR = debian/tmp/usr %: diff --git a/infra/docker/bionic/Dockerfile.aarch64 b/infra/docker/bionic/Dockerfile.aarch64 new file mode 100644 index 0000000..08d712c --- /dev/null +++ b/infra/docker/bionic/Dockerfile.aarch64 @@ -0,0 +1,92 @@ +# Copyright 2016-2020 Jing Li +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu:18.04 + +ARG UBUNTU_MIRROR + +# Install 'add-apt-repository' +RUN apt-get update && apt-get -qqy install software-properties-common + +# Git repo for latest version (github checkout@v2 action requires v2.18) +RUN add-apt-repository ppa:git-core/ppa -y + +# Build tool +RUN apt-get update && apt-get -qqy install build-essential cmake scons git g++-arm-linux-gnueabihf + +# ARM none eabi build tool +RUN apt-get update && apt-get -qqy install gcc-arm-none-eabi + +# Debian build tool +RUN apt-get update && apt-get -qqy install fakeroot devscripts debhelper python3-all + +# Install extra dependencies (Caffe, nnkit) +RUN apt-get update && apt-get -qqy install libboost-all-dev libgflags-dev libgoogle-glog-dev libatlas-base-dev libhdf5-dev + +# Install protocol buffer +RUN apt-get update && apt-get -qqy install libprotobuf-dev protobuf-compiler + +# Additonal tools +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive \ + apt-get -qqy install doxygen graphviz wget zip unzip clang-format-8 python3 python3-pip python3-venv python3-dev hdf5-tools pylint curl +RUN apt-get update && apt-get -qqy install python3.8 python3.8-venv python3.8-dev +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install yapf==0.22.0 numpy flatbuffers +RUN python3.8 -m pip install --upgrade pip +RUN python3.8 -m pip install numpy flatbuffers + +# Install google test (source) +RUN apt-get update && apt-get -qqy install libgtest-dev + +# Install build tool gcc version 8.x and set alternative link (c++17 support) +RUN apt-get update && apt-get -qqy install g++-8 g++-8-arm-linux-gnueabihf +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 80 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-8 \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-8 +RUN update-alternatives --install /usr/bin/arm-linux-gnueabihf-gcc arm-linux-gnueabihf-gcc /usr/bin/arm-linux-gnueabihf-gcc-8 80 \ + --slave /usr/bin/arm-linux-gnueabihf-g++ arm-linux-gnueabihf-g++ /usr/bin/arm-linux-gnueabihf-g++-8 \ + --slave /usr/bin/arm-linux-gnueabihf-gcov arm-linux-gnueabihf-gcov /usr/bin/arm-linux-gnueabihf-gcov-8 + +# Install lcov 1.14-2 for gcc-8 support +# Default version lcov 1.13-3 can't support gcc-8 +# lcov 1.13-4 with gcc-8 have bug: reports no coverage for class declaration +WORKDIR /root/lcov +RUN wget http://archive.ubuntu.com/ubuntu/pool/universe/l/lcov/lcov_1.14-2_all.deb +RUN apt-get update && apt-get -qqy install libperlio-gzip-perl libjson-perl +RUN dpkg -i lcov_1.14-2_all.deb +WORKDIR /root +RUN rm -rf /root/lcov + +# Build and install google test static libraries +WORKDIR /root/gtest +RUN cmake /usr/src/gtest +RUN make +RUN mv *.a /usr/lib +WORKDIR /root +RUN rm -rf gtest + +# Setup user to match host user, and give superuser permissions +ARG USER_ID=1000 +ARG GROUP_ID=${USER_ID} +RUN addgroup --gid ${GROUP_ID} ubuntu && adduser --disabled-password --gecos '' --uid ${USER_ID} --gid ${GROUP_ID} ubuntu && usermod -aG sudo ubuntu +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +RUN echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# Clean archives (to reduce image size) +RUN apt-get clean -y + +# Set user to the one we just created +USER ${USER_ID} diff --git a/infra/docker/focal/Dockerfile b/infra/docker/focal/Dockerfile index 2848579..0c6c582 100644 --- a/infra/docker/focal/Dockerfile +++ b/infra/docker/focal/Dockerfile @@ -48,6 +48,52 @@ RUN wget http://download.tizen.org/sdk/tizenstudio/official/binary/sdb_4.2.19_ub RUN unzip -d tmp sdb.zip && rm sdb.zip RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp/* +# ARM none eabi build tool +RUN apt-get update && apt-get -qqy install gcc-arm-none-eabi + +# Install java +RUN apt-get install -y --no-install-recommends openjdk-8-jdk + +# download and install Gradle +# https://services.gradle.org/distributions/ +ARG GRADLE_VERSION=6.4.1 +ARG GRADLE_DIST=bin +RUN cd /opt && \ + wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-${GRADLE_DIST}.zip && \ + unzip gradle*.zip && \ + ls -d */ | sed 's/\/*$//g' | xargs -I{} mv {} gradle && \ + rm gradle*.zip + +# download and install Android SDK +# https://developer.android.com/studio#command-tools +ARG ANDROID_SDK_VERSION=6514223 +ENV ANDROID_SDK_ROOT /opt/android-sdk +RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \ + wget -q https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_VERSION}_latest.zip && \ + unzip *tools*linux*.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \ + rm *tools*linux*.zip + +# accept the license agreements of the SDK components +RUN mkdir -p ${ANDROID_SDK_ROOT}/licenses +RUN echo 24333f8a63b6825ea9c5514f83c2829b004d1fee > ${ANDROID_SDK_ROOT}/licenses/android-sdk-license +RUN echo d56f5187479451eabf01fb78af6dfcb131a6481e >> ${ANDROID_SDK_ROOT}/licenses/android-sdk-license + +# Env variable for gradle build +ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 +ENV GRADLE_HOME /opt/gradle +ENV PATH ${PATH}:${GRADLE_HOME}/bin:${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin:${ANDROID_SDK_ROOT}/platform-tools +ENV ANDROID_HOME ${ANDROID_SDK_ROOT} + +# Install NDK +RUN sdkmanager --install "ndk;20.0.5594570" +RUN sdkmanager "platform-tools" + +# Env for ko encoding build +ENV LC_ALL "C.UTF-8" + +# setup adb server +EXPOSE 5037 + # Setup user to match host user, and give superuser permissions ARG USER_ID=1000 ARG GROUP_ID=${USER_ID} diff --git a/infra/docker/focal/Dockerfile.aarch64 b/infra/docker/focal/Dockerfile.aarch64 new file mode 100644 index 0000000..b63bbb1 --- /dev/null +++ b/infra/docker/focal/Dockerfile.aarch64 @@ -0,0 +1,62 @@ +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu:20.04 + +ARG UBUNTU_MIRROR + +# Install 'add-apt-repository' +RUN apt-get update && apt-get -qqy install software-properties-common + +# Build tool +RUN apt-get update && apt-get -qqy install build-essential cmake scons git lcov g++-arm-linux-gnueabihf + +# Debian build tool +RUN apt-get update && apt-get -qqy install fakeroot devscripts debhelper python3-all dh-python + +# Install extra dependencies (Caffe, nnkit) +RUN apt-get update && apt-get -qqy install libboost-all-dev libgflags-dev libgoogle-glog-dev libatlas-base-dev libhdf5-dev + +# Install protocol buffer +RUN apt-get update && apt-get -qqy install libprotobuf-dev protobuf-compiler + +# Additonal tools +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive \ + apt-get -qqy install doxygen graphviz wget zip unzip clang-format-8 python3 python3-pip python3-venv python3-dev hdf5-tools pylint curl +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install yapf==0.22.0 numpy flatbuffers + +# Install google test (source) +RUN apt-get update && apt-get -qqy install libgtest-dev + +# Install gbs & sdb +RUN echo 'deb [trusted=yes] http://download.tizen.org/tools/latest-release/Ubuntu_20.04/ /' | cat >> /etc/apt/sources.list +RUN apt-get update && apt-get -qqy install gbs +RUN wget http://download.tizen.org/sdk/tizenstudio/official/binary/sdb_4.2.19_ubuntu-64.zip -O sdb.zip +RUN unzip -d tmp sdb.zip && rm sdb.zip +RUN cp tmp/data/tools/sdb /usr/bin/. && rm -rf tmp/* + +# Setup user to match host user, and give superuser permissions +ARG USER_ID=1000 +ARG GROUP_ID=${USER_ID} +RUN addgroup --gid ${GROUP_ID} ubuntu && adduser --disabled-password --gecos '' --uid ${USER_ID} --gid ${GROUP_ID} ubuntu && usermod -aG sudo ubuntu +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +RUN echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# Clean archives (to reduce image size) +RUN apt-get clean -y + +# Set user to the one we just created +USER ${USER_ID} diff --git a/infra/docker/jammy/Dockerfile.aarch64 b/infra/docker/jammy/Dockerfile.aarch64 new file mode 100644 index 0000000..a6a449d --- /dev/null +++ b/infra/docker/jammy/Dockerfile.aarch64 @@ -0,0 +1,60 @@ +# Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu:jammy + +ARG UBUNTU_MIRROR + +# Install 'add-apt-repository' +RUN apt-get update && apt-get -qqy install software-properties-common + +# Build tool +RUN apt-get update && apt-get -qqy install build-essential cmake scons git lcov g++-arm-linux-gnueabihf + +# Debian build tool +RUN apt-get update && apt-get -qqy install fakeroot devscripts debhelper python3-all dh-python + +# Install extra dependencies (Caffe, nnkit) +RUN apt-get update && apt-get -qqy install libboost-all-dev libgflags-dev libgoogle-glog-dev libatlas-base-dev libhdf5-dev + +# Install protocol buffer +RUN apt-get update && apt-get -qqy install libprotobuf-dev protobuf-compiler + +# Additonal tools +# TODO install clang-format (No official clang-format-8 package for ubuntu jammy) +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive \ + apt-get -qqy install doxygen graphviz wget zip unzip python3 python3-pip python3-venv python3-dev hdf5-tools pylint curl +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install yapf==0.22.0 numpy flatbuffers + +# Install google test (source) +RUN apt-get update && apt-get -qqy install libgtest-dev + +# TODO: Install gbs & sdb +# gbs & sdb are not support ubuntu jammy yet + +# Setup user to match host user, and give superuser permissions +ARG USER_ID=1000 +ARG GROUP_ID=${USER_ID} +RUN apt-get update && apt-get -qqy install sudo +RUN addgroup --gid ${GROUP_ID} ubuntu && adduser --disabled-password --gecos '' --uid ${USER_ID} --gid ${GROUP_ID} ubuntu && usermod -aG sudo ubuntu +RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +RUN echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# Clean archives (to reduce image size) +RUN apt-get clean -y + +# Set user to the one we just created +USER ${USER_ID} diff --git a/infra/nncc/Makefile.arm32 b/infra/nncc/Makefile.arm32 index 97631fc..9ba57dd 100644 --- a/infra/nncc/Makefile.arm32 +++ b/infra/nncc/Makefile.arm32 @@ -26,7 +26,7 @@ ARM32_BUILD_ITEMS+=;pepper-csv2vec;crew ARM32_BUILD_ITEMS+=;oops;pepper-assert ARM32_BUILD_ITEMS+=;hermes;hermes-std ARM32_BUILD_ITEMS+=;loco;locop;logo-core;logo -ARM32_BUILD_ITEMS+=;safemain;mio-circle04;mio-tflite280 +ARM32_BUILD_ITEMS+=;safemain;mio-circle05;mio-tflite280;mio-circle06;mio-tflite2121 ARM32_BUILD_ITEMS+=;dio-hdf5 ARM32_BUILD_ITEMS+=;luci-compute ARM32_BUILD_ITEMS+=;foder;circle-verify;souschef;arser;vconone @@ -44,7 +44,7 @@ ARM32_HOST_ITEMS+=;pepper-csv2vec ARM32_HOST_ITEMS+=;oops ARM32_HOST_ITEMS+=;hermes;hermes-std ARM32_HOST_ITEMS+=;loco;locop;logo-core;logo -ARM32_HOST_ITEMS+=;safemain;mio-circle04;mio-tflite280 +ARM32_HOST_ITEMS+=;safemain;mio-circle05;mio-tflite280;mio-circle06;mio-tflite2121 ARM32_HOST_ITEMS+=;luci-compute ARM32_HOST_ITEMS+=;foder;circle-verify;souschef;arser;vconone ARM32_HOST_ITEMS+=;luci diff --git a/infra/nncc/cmake/buildtool/config/config_aarch64-linux.cmake b/infra/nncc/cmake/buildtool/config/config_aarch64-linux.cmake new file mode 100644 index 0000000..fcae94f --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_aarch64-linux.cmake @@ -0,0 +1,13 @@ +# +# aarch64 linux compile options +# + +message(STATUS "Building for aarch64 Linux") + +# include linux common +include("${CMAKE_CURRENT_LIST_DIR}/config_linux.cmake") + +# addition for arm-linux +set(FLAGS_COMMON ${FLAGS_COMMON} + "-march=armv8-a" + ) diff --git a/infra/nncc/cmake/buildtool/config/config_aarch64-tizen.cmake b/infra/nncc/cmake/buildtool/config/config_aarch64-tizen.cmake new file mode 100644 index 0000000..0f304ec --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_aarch64-tizen.cmake @@ -0,0 +1,17 @@ +# +# aarch64 tizen compile options +# + +message(STATUS "Building for AARCH64 Tizen") + +# Build flag for tizen +set(CMAKE_C_FLAGS_DEBUG "-O -g -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-O -g -DDEBUG") + +# TODO : add and use option_tizen if something uncommon comes up +# include linux common +include("cmake/buildtool/config/config_linux.cmake") + +# addition for aarch64-tizen +set(FLAGS_COMMON ${FLAGS_COMMON} + ) diff --git a/infra/nncc/cmake/buildtool/config/config_armv7hl-tizen.cmake b/infra/nncc/cmake/buildtool/config/config_armv7hl-tizen.cmake new file mode 100644 index 0000000..fc6876a --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_armv7hl-tizen.cmake @@ -0,0 +1,29 @@ +# +# armv7l tizen compile options +# + +message(STATUS "Building for ARMv7hl(hardfp) Tizen") + +# Build flag for tizen +set(CMAKE_C_FLAGS_DEBUG "-O -g -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-O -g -DDEBUG") + +# TODO : add and use option_tizen if something uncommon comes up +# include linux common +include("cmake/buildtool/config/config_linux.cmake") + +# addition for arm-linux +set(FLAGS_COMMON ${FLAGS_COMMON} + "-mtune=cortex-a8" + "-mfloat-abi=hard" + "-funsafe-math-optimizations" + ) + +if(BUILD_ARM32_NEON) + set(FLAGS_COMMON ${FLAGS_COMMON} + "-mfpu=neon-vfpv4" + "-ftree-vectorize" + ) +else(BUILD_ARM32_NEON) + message(STATUS "ARMv7l: NEON is disabled") +endif(BUILD_ARM32_NEON) diff --git a/infra/nncc/cmake/buildtool/config/config_armv7l-tizen.cmake b/infra/nncc/cmake/buildtool/config/config_armv7l-tizen.cmake new file mode 100644 index 0000000..b1ffe65 --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_armv7l-tizen.cmake @@ -0,0 +1,29 @@ +# +# armv7l tizen compile options +# + +message(STATUS "Building for ARMv7l(softfp) Tizen") + +# Build flag for tizen +set(CMAKE_C_FLAGS_DEBUG "-O -g -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-O -g -DDEBUG") + +# TODO : add and use option_tizen if something uncommon comes up +# include linux common +include("cmake/buildtool/config/config_linux.cmake") + +# addition for arm-linux +set(FLAGS_COMMON ${FLAGS_COMMON} + "-mtune=cortex-a8" + "-mfloat-abi=softfp" + "-funsafe-math-optimizations" + ) + +if(BUILD_ARM32_NEON) + set(FLAGS_COMMON ${FLAGS_COMMON} + "-mfpu=neon-vfpv4" + "-ftree-vectorize" + ) +else(BUILD_ARM32_NEON) + message(STATUS "ARMv7l: NEON is disabled") +endif(BUILD_ARM32_NEON) diff --git a/infra/nncc/cmake/buildtool/config/config_i686-tizen.cmake b/infra/nncc/cmake/buildtool/config/config_i686-tizen.cmake new file mode 100644 index 0000000..3929e07 --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_i686-tizen.cmake @@ -0,0 +1,17 @@ +# +# i686 tizen compile options +# + +message(STATUS "Building for i686 Tizen") + +# Build flag for tizen +set(CMAKE_C_FLAGS_DEBUG "-O -g -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-O -g -DDEBUG") + +# TODO : add and use option_tizen if something uncommon comes up +# include linux common +include("cmake/buildtool/config/config_linux.cmake") + +# addition for i686-tizen +set(FLAGS_COMMON ${FLAGS_COMMON} + ) diff --git a/infra/nncc/cmake/buildtool/config/config_x86_64-tizen.cmake b/infra/nncc/cmake/buildtool/config/config_x86_64-tizen.cmake new file mode 100644 index 0000000..0f304ec --- /dev/null +++ b/infra/nncc/cmake/buildtool/config/config_x86_64-tizen.cmake @@ -0,0 +1,17 @@ +# +# aarch64 tizen compile options +# + +message(STATUS "Building for AARCH64 Tizen") + +# Build flag for tizen +set(CMAKE_C_FLAGS_DEBUG "-O -g -DDEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "-O -g -DDEBUG") + +# TODO : add and use option_tizen if something uncommon comes up +# include linux common +include("cmake/buildtool/config/config_linux.cmake") + +# addition for aarch64-tizen +set(FLAGS_COMMON ${FLAGS_COMMON} + ) diff --git a/infra/nncc/cmake/options/options_aarch64-darwin.cmake b/infra/nncc/cmake/options/options_aarch64-darwin.cmake new file mode 100644 index 0000000..89398bd --- /dev/null +++ b/infra/nncc/cmake/options/options_aarch64-darwin.cmake @@ -0,0 +1,4 @@ +# +# aarch64 darwin cmake options +# + diff --git a/infra/nncc/cmake/options/options_aarch64-linux.cmake b/infra/nncc/cmake/options/options_aarch64-linux.cmake new file mode 100644 index 0000000..becd574 --- /dev/null +++ b/infra/nncc/cmake/options/options_aarch64-linux.cmake @@ -0,0 +1,4 @@ +# +# aarch64 linux cmake options +# + diff --git a/infra/nncc/cmake/options/options_aarch64-tizen.cmake b/infra/nncc/cmake/options/options_aarch64-tizen.cmake new file mode 100644 index 0000000..be97cb3 --- /dev/null +++ b/infra/nncc/cmake/options/options_aarch64-tizen.cmake @@ -0,0 +1,4 @@ +# +# aarch64 tizen cmake options +# + diff --git a/infra/nncc/cmake/options/options_armv7hl-tizen.cmake b/infra/nncc/cmake/options/options_armv7hl-tizen.cmake new file mode 100644 index 0000000..e787ece --- /dev/null +++ b/infra/nncc/cmake/options/options_armv7hl-tizen.cmake @@ -0,0 +1,5 @@ +# +# armv7hl tizen cmake options +# + +option(BUILD_ARM32_NEON "Use NEON for ARM32 build" ON) diff --git a/infra/nncc/cmake/options/options_armv7l-tizen.cmake b/infra/nncc/cmake/options/options_armv7l-tizen.cmake new file mode 100644 index 0000000..9a96f40 --- /dev/null +++ b/infra/nncc/cmake/options/options_armv7l-tizen.cmake @@ -0,0 +1,5 @@ +# +# armv7l tizen cmake options +# + +option(BUILD_ARM32_NEON "Use NEON for ARM32 build" ON) diff --git a/infra/nncc/cmake/options/options_i686-tizen.cmake b/infra/nncc/cmake/options/options_i686-tizen.cmake new file mode 100644 index 0000000..028efca --- /dev/null +++ b/infra/nncc/cmake/options/options_i686-tizen.cmake @@ -0,0 +1,3 @@ +# +# i686 tizen cmake options +# diff --git a/infra/nncc/cmake/options/options_x86_64-darwin.cmake b/infra/nncc/cmake/options/options_x86_64-darwin.cmake new file mode 100644 index 0000000..1a29135 --- /dev/null +++ b/infra/nncc/cmake/options/options_x86_64-darwin.cmake @@ -0,0 +1,4 @@ +# +# x86_64 darwin cmake options +# + diff --git a/infra/nncc/cmake/options/options_x86_64-tizen.cmake b/infra/nncc/cmake/options/options_x86_64-tizen.cmake new file mode 100644 index 0000000..a29a0af --- /dev/null +++ b/infra/nncc/cmake/options/options_x86_64-tizen.cmake @@ -0,0 +1,3 @@ +# +# x86_64 tizen cmake options +# diff --git a/infra/nnfw/CMakeLists.txt b/infra/nnfw/CMakeLists.txt index bb55a8f..857c15b 100644 --- a/infra/nnfw/CMakeLists.txt +++ b/infra/nnfw/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.16.3) project(nnfw) @@ -6,7 +6,7 @@ enable_testing() set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) -set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/") +set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/../lib/nnfw/odc:$ORIGIN/") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) ### CMAKE_BUILD_TYPE_LC: Build type lower case diff --git a/infra/nnfw/cmake/CfgOptionFlags.cmake b/infra/nnfw/cmake/CfgOptionFlags.cmake index b2f4da5..8d37cf2 100644 --- a/infra/nnfw/cmake/CfgOptionFlags.cmake +++ b/infra/nnfw/cmake/CfgOptionFlags.cmake @@ -20,6 +20,7 @@ option(BUILD_RUNTIME_NNFW_API_TEST "Build Runtime NNFW API Tests" ON) option(BUILD_TFLITE_RUN "Build tflite-run" ON) option(BUILD_TFLITE_VANILLA_RUN "Build tflite-vanilla-run" OFF) option(BUILD_ONERT_RUN "Build onert_run" ON) +option(BUILD_ONERT_TRAIN "Build onert_train" ON) option(BUILD_TFLITE_LOADER "Build TensorFlow Lite loader" ON) option(BUILD_CIRCLE_LOADER "Build circle loader" ON) option(BUILD_TRIX_LOADER "Build trix loader" ON) @@ -31,6 +32,8 @@ option(INSTALL_TEST_SCRIPTS "Install test scripts" ON) option(BUILD_GPU_CL "Build gpu_cl backend" OFF) option(BUILD_NPUD "Build NPU daemon" OFF) option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" ON) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" ON) # # Default build configuration for contrib # diff --git a/infra/nnfw/cmake/buildtool/config/config_linux.cmake b/infra/nnfw/cmake/buildtool/config/config_linux.cmake index 01b47ef..681d165 100644 --- a/infra/nnfw/cmake/buildtool/config/config_linux.cmake +++ b/infra/nnfw/cmake/buildtool/config/config_linux.cmake @@ -10,3 +10,8 @@ set(FLAGS_CXXONLY ${FLAGS_CXXONLY} "-Wno-ignored-attributes") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) list(APPEND FLAGS_CXXONLY "-Wno-psabi") endif() + +# Build fail on memcpy (ex. compute/cker/include/cker/Shape.h:211:16) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12.0) + list(APPEND FLAGS_CXXONLY "-Wno-error=stringop-overflow -Wno-error=array-bounds") +endif() diff --git a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake b/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake deleted file mode 100644 index 181415d..0000000 --- a/infra/nnfw/cmake/buildtool/cross/toolchain_armv7l-tizen.cmake +++ /dev/null @@ -1,56 +0,0 @@ -# -# config for arm-linux -# -include(CMakeForceCompiler) - -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR armv7l) - -set(CMAKE_C_COMPILER arm-linux-gnueabi-gcc) -set(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++) - -set(TIZEN_TOOLCHAIN "armv7l-tizen-linux-gnueabi/6.2.1") - -# where is the target environment -set(NNAS_PROJECT_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../..") -set(ROOTFS_ARM "${NNAS_PROJECT_SOURCE_DIR}/tools/cross/rootfs/armel") -include("${NNAS_PROJECT_SOURCE_DIR}/infra/cmake/modules/OptionTools.cmake") - -envoption(ROOTFS_DIR ${ROOTFS_ARM}) -if(NOT EXISTS "${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") - message(FATAL_ERROR "Please prepare RootFS for tizen ARM softfp") -endif() - -set(CMAKE_SYSROOT ${ROOTFS_DIR}) -set(CMAKE_FIND_ROOT_PATH ${ROOTFS_DIR}) - -# search for programs in the build host directories -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - -# for libraries and headers in the target directories -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -# Set cache variable to ignore try-run error by find_package(Threads REQUIRED) on cross build -set(THREADS_PTHREAD_ARG "2" CACHE STRING "Result from TRY_RUN" FORCE) - - -add_compile_options(-mthumb) -add_compile_options(-mfpu=neon-vfpv4) -add_compile_options(-mfloat-abi=softfp) -add_compile_options(--sysroot=${ROOTFS_DIR}) - -include_directories(SYSTEM ${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/) -include_directories(SYSTEM ${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}/include/c++/armv7l-tizen-linux-gnueabi) -add_compile_options(-Wno-deprecated-declarations) # compile-time option -add_compile_options(-D__extern_always_inline=inline) # compile-time option - -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -B${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L${ROOTFS_DIR}/lib") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L${ROOTFS_DIR}/usr/lib") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -B${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${ROOTFS_DIR}/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${ROOTFS_DIR}/usr/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${ROOTFS_DIR}/usr/lib/gcc/${TIZEN_TOOLCHAIN}") diff --git a/infra/nnfw/cmake/options/options_aarch64-android.cmake b/infra/nnfw/cmake/options/options_aarch64-android.cmake index 242dcd3..5de2be3 100644 --- a/infra/nnfw/cmake/options/options_aarch64-android.cmake +++ b/infra/nnfw/cmake/options/options_aarch64-android.cmake @@ -14,3 +14,5 @@ option(DOWNLOAD_OPENCL_HEADERS "Download Opencl_headers source" ON) option(DOWNLOAD_PYBIND11 "Download Pybind11 source" ON) option(BUILD_GPU_CL "Build gpu_cl backend" ON) option(BUILD_TENSORFLOW_LITE_GPU "Build TensorFlow Lite GPU delegate from the downloaded source" ON) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) diff --git a/infra/nnfw/cmake/options/options_aarch64-tizen.cmake b/infra/nnfw/cmake/options/options_aarch64-tizen.cmake index daaa4c4..cccd77f 100644 --- a/infra/nnfw/cmake/options/options_aarch64-tizen.cmake +++ b/infra/nnfw/cmake/options/options_aarch64-tizen.cmake @@ -14,3 +14,6 @@ option(ENVVAR_ONERT_CONFIG "Use environment variable for onert configuration" OF option(BUILD_NPUD "Build NPU daemon" ON) # Do not allow to use CONFIG option on Tizen option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" OFF) diff --git a/infra/nnfw/cmake/options/options_armv7hl-tizen.cmake b/infra/nnfw/cmake/options/options_armv7hl-tizen.cmake index e28801f..07dc040 100644 --- a/infra/nnfw/cmake/options/options_armv7hl-tizen.cmake +++ b/infra/nnfw/cmake/options/options_armv7hl-tizen.cmake @@ -22,3 +22,6 @@ option(BUILD_TENSORFLOW_LITE_GPU "Build TensorFlow Lite GPU delegate from the do option(BUILD_NPUD "Build NPU daemon" ON) # Do not allow to use CONFIG option on Tizen option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" OFF) diff --git a/infra/nnfw/cmake/options/options_armv7l-tizen.cmake b/infra/nnfw/cmake/options/options_armv7l-tizen.cmake index c5f4a2c..4fdcbc3 100644 --- a/infra/nnfw/cmake/options/options_armv7l-tizen.cmake +++ b/infra/nnfw/cmake/options/options_armv7l-tizen.cmake @@ -22,3 +22,6 @@ option(BUILD_TENSORFLOW_LITE_GPU "Build TensorFlow Lite GPU delegate from the do option(BUILD_NPUD "Build NPU daemon" ON) # Do not allow to use CONFIG option on Tizen option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" OFF) diff --git a/infra/nnfw/cmake/options/options_i686-tizen.cmake b/infra/nnfw/cmake/options/options_i686-tizen.cmake index 14a3d55..e51f5ed 100644 --- a/infra/nnfw/cmake/options/options_i686-tizen.cmake +++ b/infra/nnfw/cmake/options/options_i686-tizen.cmake @@ -11,3 +11,6 @@ option(GENERATE_RUNTIME_NNAPI_TESTS "Generate NNAPI operation gtest" OFF) option(ENVVAR_ONERT_CONFIG "Use environment variable for onert configuration" OFF) option(BUILD_XNNPACK "Build XNNPACK" OFF) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" OFF) diff --git a/infra/nnfw/cmake/options/options_x86_64-tizen.cmake b/infra/nnfw/cmake/options/options_x86_64-tizen.cmake index 24b4d95..70da68c 100644 --- a/infra/nnfw/cmake/options/options_x86_64-tizen.cmake +++ b/infra/nnfw/cmake/options/options_x86_64-tizen.cmake @@ -16,3 +16,6 @@ option(DOWNLOAD_OPENCL_HEADERS "Download opencl headers" OFF) option(BUILD_NPUD "Build NPU daemon" ON) # Do not allow to use CONFIG option on Tizen option(ENVVAR_NPUD_CONFIG "Use environment variable for npud configuration" OFF) + +option(BUILD_MINMAX_H5DUMPER "Build minmax h5dumper" OFF) +option(ENABLE_ONERT_TRAIN "Enable onert training feature" OFF) diff --git a/infra/nnfw/cmake/packages/LuciConfig.cmake b/infra/nnfw/cmake/packages/LuciConfig.cmake new file mode 100644 index 0000000..426556b --- /dev/null +++ b/infra/nnfw/cmake/packages/LuciConfig.cmake @@ -0,0 +1,43 @@ +# Assume that luci and related libraries and headers are installed on overlay directory + +set(Luci_FOUND FALSE) + +find_path(LUCI_HEADERS + NAMES loco.h luci/IR/CircleNode.h + PATHS ${EXT_OVERLAY_DIR}/include) + +macro(_load_library LUCI_NAME) + add_library(luci::${LUCI_NAME} SHARED IMPORTED) + find_library(LUCI_LIB_PATH_${LUCI_NAME} NAMES luci_${LUCI_NAME} PATHS ${EXT_OVERLAY_DIR}/lib) + if (NOT LUCI_LIB_PATH_${LUCI_NAME}) + return() + endif() + set_target_properties(luci::${LUCI_NAME} PROPERTIES + IMPORTED_LOCATION ${LUCI_LIB_PATH_${LUCI_NAME}} + INTERFACE_INCLUDE_DIRECTORIES ${LUCI_HEADERS}) +endmacro() + +_load_library(env) +_load_library(export) +_load_library(import) +_load_library(lang) +_load_library(logex) +_load_library(log) +_load_library(partition) +_load_library(pass) +_load_library(plan) +_load_library(profile) +_load_library(service) + +# Need luci::loco to avoid "DSO missing from command line" link error +# TODO Find better way to do this +add_library(luci::loco SHARED IMPORTED) +find_library(LOCO_LIB_PATH NAMES loco PATHS ${EXT_OVERLAY_DIR}/lib) +if (NOT LOCO_LIB_PATH) + return() +endif() +set_target_properties(luci::loco PROPERTIES + IMPORTED_LOCATION ${LOCO_LIB_PATH} + INTERFACE_INCLUDE_DIRECTORIES ${LUCI_HEADERS}) + +set(Luci_FOUND TRUE) diff --git a/infra/packaging/build b/infra/packaging/build index 0c1370f..16bce7e 100644 --- a/infra/packaging/build +++ b/infra/packaging/build @@ -8,7 +8,7 @@ if [[ -z "${NNAS_PROJECT_PATH}" ]]; then fi # The default preset -PRESET="20221125" +PRESET="20230413" # Test is enabled by default DISABLE_TEST=false diff --git a/infra/packaging/preset/20230413 b/infra/packaging/preset/20230413 new file mode 100644 index 0000000..85ce6cb --- /dev/null +++ b/infra/packaging/preset/20230413 @@ -0,0 +1,66 @@ +#!/bin/bash + +# NOTE purpose of this file is static analysis only +# new official preset will be added when new programs are ready + +PRESET="20230413" + +function preset_configure() +{ + REQUIRED_UNITS=() + # Common Libraries + REQUIRED_UNITS+=("angkor" "cwrap" "pepper-str" "pepper-strcast" "pp") + REQUIRED_UNITS+=("oops" "pepper-assert" "pepper-csv2vec" "foder" "crew") + REQUIRED_UNITS+=("souschef") + REQUIRED_UNITS+=("safemain") + REQUIRED_UNITS+=("arser") + REQUIRED_UNITS+=("vconone") + # Hermes Logging Framework + REQUIRED_UNITS+=("hermes" "hermes-std") + # loco IR and related utilities + REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") + # Flatbuffer I/O + REQUIRED_UNITS+=("mio-tflite280" "mio-circle05" "mio-tflite2121" "mio-circle06") + # Data I/O + REQUIRED_UNITS+=("dio-hdf5") + # Compute + REQUIRED_UNITS+=("luci-compute") + # Circle compiler library (.circle -> .circle) + REQUIRED_UNITS+=("luci") + # Python interface for circle schema + REQUIRED_UNITS+=("pics") + # Tools + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") + REQUIRED_UNITS+=("circle-tensordump" "circledump") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") + REQUIRED_UNITS+=("luci-eval-driver") + REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5" "circle-mpqsolver") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") + REQUIRED_UNITS+=("one-cmds") + REQUIRED_UNITS+=("bcq-tools") + REQUIRED_UNITS+=("dalgona") + REQUIRED_UNITS+=("visq") + REQUIRED_UNITS+=("circle-opselector") + + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + + NPROC=${NPROC:-$(cat /proc/cpuinfo | grep -c processor)} + + # TODO Use "nncc configure" and "nncc build" + cmake \ + -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \ + -DCMAKE_BUILD_TYPE=release \ + -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \ + -DEXTERNALS_BUILD_THREADS=$((NPROC/2)) \ + ${EXTRA_OPTIONS[@]} \ + "${NNAS_PROJECT_PATH}/infra/nncc" +} + +function preset_install() +{ + # Install tf2nnpkg + install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.${PRESET}" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg" +} diff --git a/infra/packaging/preset/20230413_windows b/infra/packaging/preset/20230413_windows new file mode 100644 index 0000000..8015de8 --- /dev/null +++ b/infra/packaging/preset/20230413_windows @@ -0,0 +1,80 @@ +#!/bin/bash + +PRESET="20230413" + +function preset_configure() +{ + REQUIRED_UNITS=() + # Common Libraries + REQUIRED_UNITS+=("angkor" "cwrap" "pepper-str" "pepper-strcast" "pp") + REQUIRED_UNITS+=("oops" "pepper-assert" "pepper-csv2vec" "foder" "crew") + REQUIRED_UNITS+=("souschef") + REQUIRED_UNITS+=("safemain") + REQUIRED_UNITS+=("arser") + REQUIRED_UNITS+=("vconone") + # Hermes Logging Framework + REQUIRED_UNITS+=("hermes" "hermes-std") + # loco IR and related utilities + REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") + # Flatbuffer I/O + REQUIRED_UNITS+=("mio-tflite280" "mio-circle05" "mio-tflite2121" "mio-circle06") + # Data I/O + REQUIRED_UNITS+=("dio-hdf5") + # Compute + REQUIRED_UNITS+=("luci-compute") + # Circle compiler library (.circle -> .circle) + REQUIRED_UNITS+=("luci") + # Python interface for circle schema + REQUIRED_UNITS+=("pics") + # Tools + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") + REQUIRED_UNITS+=("circle-tensordump" "circledump") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") + REQUIRED_UNITS+=("luci-eval-driver") + REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5" "circle-mpqsolver") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") + REQUIRED_UNITS+=("one-cmds") + REQUIRED_UNITS+=("bcq-tools") + REQUIRED_UNITS+=("dalgona") + REQUIRED_UNITS+=("visq") + + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + + NPROC=$(cat /proc/cpuinfo | grep -c processor) + + # TODO Use "nncc configure" and "nncc build" + cmake \ + -G "MSYS Makefiles" \ + -DUSE_PROTOBUF_LEGACY_IMPORT=ON \ + -DCMAKE_EXE_LINKER_FLAGS="-Wl,--allow-multiple-definition" \ + -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-multiple-definition" \ + -DENABLE_TEST=OFF \ + -DDOWNLOAD_GTEST=OFF \ + -DBUILD_GTEST=OFF \ + -DCMAKE_C_COMPILER=gcc \ + -DCMAKE_CXX_COMPILER=g++ \ + -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \ + -DCMAKE_BUILD_TYPE=release \ + -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \ + -DEXTERNALS_BUILD_THREADS=$((NPROC/2)) \ + ${EXTRA_OPTIONS[@]} \ + "${NNAS_PROJECT_PATH}/infra/nncc" +} + +function preset_install() +{ + # Install libraries to bin/ for Windows release + mv ${NNCC_INSTALL_PREFIX}/lib/*.dll ${NNCC_INSTALL_PREFIX}/bin + rm -rf ${NNCC_INSTALL_PREFIX}/lib + + # Install tf2nnpkg + install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.${PRESET}" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg" + + # Though you have to install tensorflow to run 'tf2tfliteV2', + # tensorflow can't be installed in mingw. First, You can install tensorflow + # from Window native CMD(run as administrator) with python virtual environment. + # And, you must copy it to "${NNAS_INSTALL_PREFIX}/bin/venv" +} diff --git a/infra/packaging/preset/20230907 b/infra/packaging/preset/20230907 new file mode 100644 index 0000000..44bc4e0 --- /dev/null +++ b/infra/packaging/preset/20230907 @@ -0,0 +1,66 @@ +#!/bin/bash + +# NOTE purpose of this file is static analysis only +# new official preset will be added when new programs are ready + +PRESET="20230907" + +function preset_configure() +{ + REQUIRED_UNITS=() + # Common Libraries + REQUIRED_UNITS+=("angkor" "cwrap" "pepper-str" "pepper-strcast" "pp") + REQUIRED_UNITS+=("oops" "pepper-assert" "pepper-csv2vec" "foder" "crew") + REQUIRED_UNITS+=("souschef") + REQUIRED_UNITS+=("safemain") + REQUIRED_UNITS+=("arser") + REQUIRED_UNITS+=("vconone") + # Hermes Logging Framework + REQUIRED_UNITS+=("hermes" "hermes-std") + # loco IR and related utilities + REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") + # Flatbuffer I/O + REQUIRED_UNITS+=("mio-tflite2121" "mio-circle06") + # Data I/O + REQUIRED_UNITS+=("dio-hdf5") + # Compute + REQUIRED_UNITS+=("luci-compute") + # Circle compiler library (.circle -> .circle) + REQUIRED_UNITS+=("luci") + # Python interface for circle schema + REQUIRED_UNITS+=("pics") + # Tools + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") + REQUIRED_UNITS+=("circle-tensordump" "circledump") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") + REQUIRED_UNITS+=("luci-eval-driver") + REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5" "circle-mpqsolver") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") + REQUIRED_UNITS+=("one-cmds") + REQUIRED_UNITS+=("bcq-tools") + REQUIRED_UNITS+=("dalgona") + REQUIRED_UNITS+=("visq") + REQUIRED_UNITS+=("circle-opselector") + + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + + NPROC=${NPROC:-$(cat /proc/cpuinfo | grep -c processor)} + + # TODO Use "nncc configure" and "nncc build" + cmake \ + -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \ + -DCMAKE_BUILD_TYPE=release \ + -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \ + -DEXTERNALS_BUILD_THREADS=$((NPROC/2)) \ + ${EXTRA_OPTIONS[@]} \ + "${NNAS_PROJECT_PATH}/infra/nncc" +} + +function preset_install() +{ + # Install tf2nnpkg + install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.${PRESET}" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg" +} diff --git a/infra/packaging/preset/20230907_windows b/infra/packaging/preset/20230907_windows new file mode 100644 index 0000000..5dcb36a --- /dev/null +++ b/infra/packaging/preset/20230907_windows @@ -0,0 +1,80 @@ +#!/bin/bash + +PRESET="20230907" + +function preset_configure() +{ + REQUIRED_UNITS=() + # Common Libraries + REQUIRED_UNITS+=("angkor" "cwrap" "pepper-str" "pepper-strcast" "pp") + REQUIRED_UNITS+=("oops" "pepper-assert" "pepper-csv2vec" "foder" "crew") + REQUIRED_UNITS+=("souschef") + REQUIRED_UNITS+=("safemain") + REQUIRED_UNITS+=("arser") + REQUIRED_UNITS+=("vconone") + # Hermes Logging Framework + REQUIRED_UNITS+=("hermes" "hermes-std") + # loco IR and related utilities + REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo") + # Flatbuffer I/O + REQUIRED_UNITS+=("mio-tflite2121" "mio-circle06") + # Data I/O + REQUIRED_UNITS+=("dio-hdf5") + # Compute + REQUIRED_UNITS+=("luci-compute") + # Circle compiler library (.circle -> .circle) + REQUIRED_UNITS+=("luci") + # Python interface for circle schema + REQUIRED_UNITS+=("pics") + # Tools + REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef") + REQUIRED_UNITS+=("circle-tensordump" "circledump") + REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter") + REQUIRED_UNITS+=("luci-eval-driver") + REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5" "circle-mpqsolver") + REQUIRED_UNITS+=("circle-eval-diff" "circle-interpreter") + REQUIRED_UNITS+=("circle-partitioner" "circle-operator") + REQUIRED_UNITS+=("one-cmds") + REQUIRED_UNITS+=("bcq-tools") + REQUIRED_UNITS+=("dalgona") + REQUIRED_UNITS+=("visq") + + # Dependent modules needed for build + REQUIRED_UNITS+=("circlechef") + REQUIRED_UNITS+=("circle-verify") + + NPROC=$(cat /proc/cpuinfo | grep -c processor) + + # TODO Use "nncc configure" and "nncc build" + cmake \ + -G "MSYS Makefiles" \ + -DUSE_PROTOBUF_LEGACY_IMPORT=ON \ + -DCMAKE_EXE_LINKER_FLAGS="-Wl,--allow-multiple-definition" \ + -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--allow-multiple-definition" \ + -DENABLE_TEST=OFF \ + -DDOWNLOAD_GTEST=OFF \ + -DBUILD_GTEST=OFF \ + -DCMAKE_C_COMPILER=gcc \ + -DCMAKE_CXX_COMPILER=g++ \ + -DCMAKE_INSTALL_PREFIX="${NNCC_INSTALL_PREFIX}" \ + -DCMAKE_BUILD_TYPE=release \ + -DBUILD_WHITELIST=$(join_by ";" "${REQUIRED_UNITS[@]}") \ + -DEXTERNALS_BUILD_THREADS=$((NPROC/2)) \ + ${EXTRA_OPTIONS[@]} \ + "${NNAS_PROJECT_PATH}/infra/nncc" +} + +function preset_install() +{ + # Install libraries to bin/ for Windows release + mv ${NNCC_INSTALL_PREFIX}/lib/*.dll ${NNCC_INSTALL_PREFIX}/bin + rm -rf ${NNCC_INSTALL_PREFIX}/lib + + # Install tf2nnpkg + install -T -m 755 -D "${SCRIPT_PATH}/res/tf2nnpkg.${PRESET}" "${NNAS_INSTALL_PREFIX}/bin/tf2nnpkg" + + # Though you have to install tensorflow to run 'tf2tfliteV2', + # tensorflow can't be installed in mingw. First, You can install tensorflow + # from Window native CMD(run as administrator) with python virtual environment. + # And, you must copy it to "${NNAS_INSTALL_PREFIX}/bin/venv" +} diff --git a/infra/packaging/res/tf2nnpkg.20230413 b/infra/packaging/res/tf2nnpkg.20230413 new file mode 100644 index 0000000..a7446e6 --- /dev/null +++ b/infra/packaging/res/tf2nnpkg.20230413 @@ -0,0 +1,109 @@ +#!/bin/bash + +set -e + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +command_exists() { + if [ "$#" -le 0 ]; then + return 1 + fi + command -v "$@" > /dev/null 2>&1 +} + +usage() +{ + echo "Convert TensorFlow model to nnpackage." + echo "Usage: tf2nnpkg" + echo " --info " + echo " --graphdef " + echo " -o " + echo " --v2 (optional) Use TF 2.x interface" + exit 255 +} + +TF_INTERFACE="--v1" + +# Parse command-line arguments +# +while [ "$#" -ne 0 ]; do + CUR="$1" + + case $CUR in + '--help') + usage + ;; + '--info') + export INFO_FILE="$2" + shift 2 + ;; + '--graphdef') + export GRAPHDEF_FILE="$2" + shift 2 + ;; + '-o') + export OUTPUT_DIR="$2" + shift 2 + ;; + '--v2') + TF_INTERFACE="--v2" + shift + ;; + *) + echo "${CUR}" + shift + ;; + esac +done + +if [ -z ${GRAPHDEF_FILE} ] || [ ! -e ${GRAPHDEF_FILE} ]; then + echo "pb is not found. Please check --graphdef is correct." + exit 2 +fi + +if [ -z ${INFO_FILE} ] || [ ! -e ${INFO_FILE} ]; then + echo "info is not found. Please check --info is correct." + exit 2 +fi + +if [ -z ${OUTPUT_DIR} ]; then + echo "output directory is not specifed. Please check -o is correct.." + exit 2 +fi + +FILE_BASE=$(basename ${GRAPHDEF_FILE}) +MODEL_NAME="${FILE_BASE%.*}" +TMPDIR=$(mktemp -d) +trap "{ rm -rf $TMPDIR; }" EXIT + +# activate python virtual environment +VIRTUALENV_LINUX="${ROOT}/bin/venv/bin/activate" +VIRTUALENV_WINDOWS="${ROOT}/bin/venv/Scripts/activate" + +if [ -e ${VIRTUALENV_LINUX} ]; then + source ${VIRTUALENV_LINUX} +elif [ -e ${VIRTUALENV_WINDOWS} ]; then + source ${VIRTUALENV_WINDOWS} +fi + +# parse inputs, outputs from info file +INPUT=$(awk -F, '/^input/ { print $2 }' ${INFO_FILE} | cut -d: -f1 | tr -d ' ' | paste -d, -s) +OUTPUT=$(awk -F, '/^output/ { print $2 }' ${INFO_FILE} | cut -d: -f1 | tr -d ' ' | paste -d, -s) + +INPUT_SHAPES=$(grep ^input ${INFO_FILE} | cut -d "[" -f2 | cut -d "]" -f1 | tr -d ' ' | xargs | tr ' ' ':') + +ONE_IMPORT_BCQ_SCRIPT="${ROOT}/bin/one-import-bcq ${TF_INTERFACE} " +ONE_IMPORT_BCQ_SCRIPT+="-i ${GRAPHDEF_FILE} " +ONE_IMPORT_BCQ_SCRIPT+="-o ${TMPDIR}/${MODEL_NAME}.tmp.circle " +ONE_IMPORT_BCQ_SCRIPT+="-I ${INPUT} " +ONE_IMPORT_BCQ_SCRIPT+="-O ${OUTPUT} " +if [ ! -z ${INPUT_SHAPES} ]; then + ONE_IMPORT_BCQ_SCRIPT+="-s ${INPUT_SHAPES} " +fi + +${ONE_IMPORT_BCQ_SCRIPT} + +# optimize +"${ROOT}/bin/circle2circle" --resolve_customop_add "${TMPDIR}/${MODEL_NAME}.tmp.circle" "${TMPDIR}/${MODEL_NAME}.circle" + +"${ROOT}/bin/model2nnpkg" -o "${OUTPUT_DIR}" -m "${TMPDIR}/${MODEL_NAME}.circle" diff --git a/infra/packaging/res/tf2nnpkg.20230907 b/infra/packaging/res/tf2nnpkg.20230907 new file mode 100644 index 0000000..a7446e6 --- /dev/null +++ b/infra/packaging/res/tf2nnpkg.20230907 @@ -0,0 +1,109 @@ +#!/bin/bash + +set -e + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +command_exists() { + if [ "$#" -le 0 ]; then + return 1 + fi + command -v "$@" > /dev/null 2>&1 +} + +usage() +{ + echo "Convert TensorFlow model to nnpackage." + echo "Usage: tf2nnpkg" + echo " --info " + echo " --graphdef " + echo " -o " + echo " --v2 (optional) Use TF 2.x interface" + exit 255 +} + +TF_INTERFACE="--v1" + +# Parse command-line arguments +# +while [ "$#" -ne 0 ]; do + CUR="$1" + + case $CUR in + '--help') + usage + ;; + '--info') + export INFO_FILE="$2" + shift 2 + ;; + '--graphdef') + export GRAPHDEF_FILE="$2" + shift 2 + ;; + '-o') + export OUTPUT_DIR="$2" + shift 2 + ;; + '--v2') + TF_INTERFACE="--v2" + shift + ;; + *) + echo "${CUR}" + shift + ;; + esac +done + +if [ -z ${GRAPHDEF_FILE} ] || [ ! -e ${GRAPHDEF_FILE} ]; then + echo "pb is not found. Please check --graphdef is correct." + exit 2 +fi + +if [ -z ${INFO_FILE} ] || [ ! -e ${INFO_FILE} ]; then + echo "info is not found. Please check --info is correct." + exit 2 +fi + +if [ -z ${OUTPUT_DIR} ]; then + echo "output directory is not specifed. Please check -o is correct.." + exit 2 +fi + +FILE_BASE=$(basename ${GRAPHDEF_FILE}) +MODEL_NAME="${FILE_BASE%.*}" +TMPDIR=$(mktemp -d) +trap "{ rm -rf $TMPDIR; }" EXIT + +# activate python virtual environment +VIRTUALENV_LINUX="${ROOT}/bin/venv/bin/activate" +VIRTUALENV_WINDOWS="${ROOT}/bin/venv/Scripts/activate" + +if [ -e ${VIRTUALENV_LINUX} ]; then + source ${VIRTUALENV_LINUX} +elif [ -e ${VIRTUALENV_WINDOWS} ]; then + source ${VIRTUALENV_WINDOWS} +fi + +# parse inputs, outputs from info file +INPUT=$(awk -F, '/^input/ { print $2 }' ${INFO_FILE} | cut -d: -f1 | tr -d ' ' | paste -d, -s) +OUTPUT=$(awk -F, '/^output/ { print $2 }' ${INFO_FILE} | cut -d: -f1 | tr -d ' ' | paste -d, -s) + +INPUT_SHAPES=$(grep ^input ${INFO_FILE} | cut -d "[" -f2 | cut -d "]" -f1 | tr -d ' ' | xargs | tr ' ' ':') + +ONE_IMPORT_BCQ_SCRIPT="${ROOT}/bin/one-import-bcq ${TF_INTERFACE} " +ONE_IMPORT_BCQ_SCRIPT+="-i ${GRAPHDEF_FILE} " +ONE_IMPORT_BCQ_SCRIPT+="-o ${TMPDIR}/${MODEL_NAME}.tmp.circle " +ONE_IMPORT_BCQ_SCRIPT+="-I ${INPUT} " +ONE_IMPORT_BCQ_SCRIPT+="-O ${OUTPUT} " +if [ ! -z ${INPUT_SHAPES} ]; then + ONE_IMPORT_BCQ_SCRIPT+="-s ${INPUT_SHAPES} " +fi + +${ONE_IMPORT_BCQ_SCRIPT} + +# optimize +"${ROOT}/bin/circle2circle" --resolve_customop_add "${TMPDIR}/${MODEL_NAME}.tmp.circle" "${TMPDIR}/${MODEL_NAME}.circle" + +"${ROOT}/bin/model2nnpkg" -o "${OUTPUT_DIR}" -m "${TMPDIR}/${MODEL_NAME}.circle" diff --git a/infra/scripts/compiler_modules.sh b/infra/scripts/compiler_modules.sh index 55e48f4..8b361a7 100644 --- a/infra/scripts/compiler_modules.sh +++ b/infra/scripts/compiler_modules.sh @@ -11,7 +11,7 @@ DEBUG_BUILD_ITEMS+=";oops;pepper-assert;pepper-csv2vec" DEBUG_BUILD_ITEMS+=";hermes;hermes-std" DEBUG_BUILD_ITEMS+=";loco;locop;locomotiv;logo-core;logo" DEBUG_BUILD_ITEMS+=";foder;crew;souschef;arser;vconone" -DEBUG_BUILD_ITEMS+=";safemain;mio-circle04;mio-tflite280;dio-hdf5" +DEBUG_BUILD_ITEMS+=";safemain;mio-circle05;mio-tflite280;mio-circle06;mio-tflite2121;dio-hdf5" DEBUG_BUILD_ITEMS+=";luci-compute" DEBUG_BUILD_ITEMS+=";tflite2circle" DEBUG_BUILD_ITEMS+=";luci" @@ -35,3 +35,4 @@ DEBUG_BUILD_ITEMS+=";circle-operator-test" DEBUG_BUILD_ITEMS+=";circle-interpreter;circle-interpreter-test" DEBUG_BUILD_ITEMS+=";dalgona;dalgona-test" DEBUG_BUILD_ITEMS+=";visq" +DEBUG_BUILD_ITEMS+=";circle-mpqsolver" diff --git a/infra/scripts/docker_build_nncc.sh b/infra/scripts/docker_build_nncc.sh index 2e603b5..dd9d0bd 100755 --- a/infra/scripts/docker_build_nncc.sh +++ b/infra/scripts/docker_build_nncc.sh @@ -67,7 +67,7 @@ tar -zcf ${ARCHIVE_PATH}/nncc-test-package.tar.gz -C ${NNCC_INSTALL_PREFIX} ./te if [ -z ${RELEASE_VERSION} ] || [ ${RELEASE_VERSION} == "nightly" ]; then ./nncc docker-run /bin/bash -c \ - 'dch -v $($(pwd)/tools/release_tool/onert_version.sh)~$(date "+%y%m%d%H") "nightly release" -D $(lsb_release --short --codename)' + 'dch -v $(${PWD}/${NNCC_INSTALL_PREFIX}/bin/one-version)~$(date "+%y%m%d%H") "nightly release" -D $(lsb_release --short --codename)' ./nncc docker-run dch -r '' fi diff --git a/infra/scripts/docker_collect_nnpkg_resources.sh b/infra/scripts/docker_collect_nnpkg_resources.sh index 9fb7def..8a73dd3 100755 --- a/infra/scripts/docker_collect_nnpkg_resources.sh +++ b/infra/scripts/docker_collect_nnpkg_resources.sh @@ -73,7 +73,7 @@ REQUIRED_UNITS+=("luci-compute") # Circle compiler library (.circle -> .circle) REQUIRED_UNITS+=("luci") # Flatbuffer I/O -REQUIRED_UNITS+=("mio-tflite280" "mio-circle04") +REQUIRED_UNITS+=("mio-tflite280" "mio-circle05" "mio-tflite2121" "mio-circle06") # Tools REQUIRED_UNITS+=("tflite2circle" "circle2circle" "luci-interpreter") REQUIRED_UNITS+=("souschef" "tflchef" "circlechef" "circle-verify") diff --git a/nnpackage/schema/circle_schema.fbs b/nnpackage/schema/circle_schema.fbs index 8ad444d..cdc1036 100644 --- a/nnpackage/schema/circle_schema.fbs +++ b/nnpackage/schema/circle_schema.fbs @@ -1,4 +1,4 @@ -// Copyright (c) 2019~2022 Samsung Electronics Co., Ltd. All Rights Reserved +// Copyright (c) 2019~2023 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"); @@ -29,6 +29,8 @@ // Version 0.2: BCQ_GATHER and BCQ_FULLY_CONNECTED are added. // Version 0.3: SHUFFLED16x1FLOAT32 is added. // Version 0.4: Base up to TensorFlow Lite v2.7.0 schema. +// Version 0.5: Base up to TensorFlow Lite v2.10.1 schema. +// Version 0.6: Base up to TensorFlow Lite v2.13.0 schema. namespace circle; @@ -61,6 +63,8 @@ enum TensorType : byte { RESOURCE = 13, VARIANT = 14, UINT32 = 15, + UINT16 = 16, + INT4 = 17, } // Custom quantization parameters for experimenting with new quantization @@ -200,6 +204,16 @@ table SparsityParameters { dim_metadata:[DimensionMetadata]; } +// The nested tensor type for VARIANT type. +table VariantSubType { + // The tensor shape. + shape:[int]; + type:TensorType; + // If false, the rank or the number of tensor dimensions is unknown. + // If false, "shape" must be []. + has_rank: bool = false; +} + table Tensor { // The tensor shape. The meaning of each entry is operator-specific but // builtin ops use: [batch size, height, width, number of channels] (That's @@ -227,6 +241,16 @@ table Tensor { // Encodes `shape` with unknown dimensions. Unknown dimensions are // represented with -1. shape_signature:[int]; // Optional. + + // If false, the rank or the number of tensor dimensions is unknown. + // If false, "shape" must be []. + has_rank: bool = false; + + // The nested Tensor types for VARIANT type. This is always empty for + // non-VARIANT types. This is optional because the nested type can be omitted. + // Currently only 1 subtype is supported. The field is defined as an array for + // flexibility of supporting multiple subtypes in the future. + variant_tensors:[VariantSubType]; } // A list of builtin operators. Builtin operators are slightly faster than custom @@ -391,6 +415,21 @@ enum BuiltinOperator : int32 { ASSIGN_VARIABLE = 144, BROADCAST_ARGS = 145, RANDOM_STANDARD_NORMAL = 146, + BUCKETIZE = 147, + RANDOM_UNIFORM = 148, + MULTINOMIAL = 149, + GELU = 150, + DYNAMIC_UPDATE_SLICE = 151, + RELU_0_TO_1 = 152, + UNSORTED_SEGMENT_PROD = 153, + UNSORTED_SEGMENT_MAX = 154, + UNSORTED_SEGMENT_SUM = 155, + ATAN2 = 156, + UNSORTED_SEGMENT_MIN = 157, + SIGN = 158, + BITCAST = 159, + BITWISE_XOR = 160, + RIGHT_SHIFT = 161, } // LINT.ThenChange(nnapi_linter/linter.proto) @@ -510,6 +549,18 @@ union BuiltinOptions { ReadVariableOptions, AssignVariableOptions, RandomOptions, + BucketizeOptions, + GeluOptions, + DynamicUpdateSliceOptions, + UnsortedSegmentProdOptions, + UnsortedSegmentMaxOptions, + UnsortedSegmentMinOptions, + UnsortedSegmentSumOptions, + ATan2Options, + SignOptions, + BitcastOptions, + BitwiseXorOptions, + RightShiftOptions, BCQGatherOptions = 252, BCQFullyConnectedOptions = 253, InstanceNormOptions = 254, @@ -704,8 +755,11 @@ table UnidirectionalSequenceLSTMOptions { // If true then first dimension is sequence, otherwise batch. time_major:bool; - // Parameter for Unidirectional Sequence LSTM version 4. + // Parameter for Unidirectional Sequence LSTM version 3. asymmetric_quantize_inputs:bool; + + // Parameter for unidirectional sequence RNN version 4. + diagonal_recurrent_tensors:bool; } table BidirectionalSequenceLSTMOptions { @@ -884,9 +938,13 @@ table SliceOptions { } table TransposeConvOptions { + // Parameters supported by version 1, 2, 3: padding:Padding; stride_w:int; stride_h:int; + + // Parameters supported by version 4: + fused_activation_function:ActivationFunctionType = NONE; } table ExpandDimsOptions { @@ -1100,8 +1158,46 @@ table AssignVariableOptions { } table RandomOptions { - seed: int; - seed2: int; + seed: long; + seed2: long; +} + +table BucketizeOptions { + boundaries: [float]; // The bucket boundaries. +} + +table GeluOptions { + approximate: bool; +} + +table DynamicUpdateSliceOptions { +} + +table UnsortedSegmentProdOptions { +} + +table UnsortedSegmentMaxOptions { +} + +table UnsortedSegmentSumOptions { +} + +table ATan2Options { +} + +table UnsortedSegmentMinOptions{ +} + +table SignOptions { +} + +table BitcastOptions { +} + +table BitwiseXorOptions { +} + +table RightShiftOptions { } table BCQGatherOptions { diff --git a/onert-micro/CMakeLists.txt b/onert-micro/CMakeLists.txt index d5ea95d..416281d 100644 --- a/onert-micro/CMakeLists.txt +++ b/onert-micro/CMakeLists.txt @@ -7,33 +7,49 @@ find_program(ARM_C_COMPILER_PATH ${ARM_C_COMPILER}) if (NOT ARM_C_COMPILER_PATH) message(STATUS "Build luci-micro: FALSE(ARM compiler is NOT FOUND)") - return() endif () -nnas_find_package(FlatBuffers EXACT 2.0 QUIET) - -if (NOT FlatBuffers_FOUND) - message(STATUS "Build luci-micro: FALSE(FlatBuffers 2.0 NOT FOUND)") - return() -endif (NOT FlatBuffers_FOUND) - -message(STATUS "Build luci-micro: TRUE") +if (NOT_BUILD_EXTERNALS) + message(STATUS "USE LOCAL EXTERNAL") -set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/res/CircleSchema/0.4/circle_schema.fbs") + set(ONERT_MICRO_EXTERNAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/externals") + set(Flatbuffers_DIR "${ONERT_MICRO_EXTERNAL_DIR}") + set(FlatBuffersSource_DIR "${Flatbuffers_DIR}") + set (EXT_OVERLAY_DIR "${ONERT_MICRO_EXTERNAL_DIR}") + set(GENERATED_INCLUDE_DIR "${ONERT_MICRO_EXTERNAL_DIR}/gen") -# NOTE Copy circle_schema.fbs as schema.fbs to generate "schema_generated.fbs" instead of "circle_schema_generated.fbs" -add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" - COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS "${SCHEMA_FILE}" - ) - -FlatBuffers_Target(luci_micro_circle_schema - OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/circle-generated/circle" - INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" - SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" - SCHEMA_FILES "schema.fbs" - ) + add_subdirectory(externals) +else() + message(STATUS "DOWNLOAD AND BUILD EXTERNALS") + + nnas_find_package(FlatBuffers EXACT 2.0 QUIET) + + if (NOT FlatBuffers_FOUND) + message(STATUS "Build luci-micro: FALSE(FlatBuffers 2.0 NOT FOUND)") + return() + endif (NOT FlatBuffers_FOUND) + + message(STATUS "Build luci-micro: TRUE") + + set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/res/CircleSchema/0.4/circle_schema.fbs") + + # NOTE Copy circle_schema.fbs as schema.fbs to generate "schema_generated.fbs" instead of "circle_schema_generated.fbs" + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" + COMMAND ${CMAKE_COMMAND} -E copy "${SCHEMA_FILE}" schema.fbs + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS "${SCHEMA_FILE}" + ) + + FlatBuffers_Target(luci_micro_circle_schema + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen/circle-generated/circle" + INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen" + SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}" + SCHEMA_FILES "schema.fbs" + ) + set(Flatbuffers_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../overlay/lib/cmake/flatbuffers") + set (EXT_OVERLAY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../overlay") + set(GENERATED_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen") +endif() set(LUCI_INTERPRETER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/luci-interpreter/include") add_subdirectory(luci-interpreter/src/core/reader) @@ -70,8 +86,9 @@ set(CMAKE_ARM_OPTIONS -DBUILD_CMSIS_NN_FUNCTIONS=ON -DTARGET_CPU=${TARGET_CPU} -DTARGET_ARCH=${TARGET_ARCH} - "-DEXT_OVERLAY_DIR=${CMAKE_CURRENT_BINARY_DIR}/../../overlay" - "-DFlatbuffers_DIR=${CMAKE_CURRENT_BINARY_DIR}/../../overlay/lib/cmake/flatbuffers" + "-DEXT_OVERLAY_DIR=${EXT_OVERLAY_DIR}" + "-DFlatbuffers_DIR=${Flatbuffers_DIR}" + "-DFlatBuffersSource_DIR=${FlatBuffersSource_DIR}" "-DCMAKE_TOOLCHAIN_FILE=${NNAS_PROJECT_SOURCE_DIR}/infra/onert-micro/cmake/buildtool/config/arm-none-eabi-gcc.cmake" "-DLUCI_INTERPRETER_PAL_DIR=${LUCI_INTERPRETER_PAL_DIR}" "-DNNAS_PROJECT_SOURCE_DIR=${NNAS_PROJECT_SOURCE_DIR}" @@ -85,7 +102,7 @@ set(CMAKE_ARM_OPTIONS -DBUILD_GTEST=OFF "-DNNAS_ROOT=${NNAS_PROJECT_SOURCE_DIR}" -DENABLE_STRICT_BUILD=OFF - "-DGENERATED_INCLUDE_DIR=${CMAKE_CURRENT_BINARY_DIR}/gen" + "-DGENERATED_INCLUDE_DIR=${GENERATED_INCLUDE_DIR}" ) if (GENERATE_KERNELS_LIST_FROM) @@ -93,6 +110,17 @@ if (GENERATE_KERNELS_LIST_FROM) list(APPEND CMAKE_ARM_OPTIONS "-DLUCI_INTERPRETER_KERNELS_BUILD_LIST=${GENERATED_KERNELS_LIST_PATH}") endif () +if (NOT_BUILD_EXTERNALS) + list(APPEND CMAKE_ARM_OPTIONS "-DNOT_BUILD_EXTERNALS=ON") +else() + list(APPEND CMAKE_ARM_OPTIONS "-DNOT_BUILD_EXTERNALS=OFF") +endif () + +if (ENABLE_ONERT_MICRO_TEST) + message(STATUS "Enable tests") + list(APPEND CMAKE_ARM_OPTIONS "-DENABLE_TEST=ON") +endif () + if (DIS_QUANT) message(STATUS "ONERT-MICRO will not use part for QUANTIZED models") add_definitions(-DDIS_QUANT) @@ -105,6 +133,11 @@ if (DIS_FLOAT) list(APPEND CMAKE_ARM_OPTIONS "-DDIS_FLOAT=ON") endif() +if (DIS_DYN_SHAPES) + message(STATUS "ONERT-MICRO will not use dynamic shapes") + add_definitions(-DDIS_DYN_SHAPES) +endif() + set(MICRO_ARM_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/standalone_arm") file(MAKE_DIRECTORY "${MICRO_ARM_BUILD_DIR}") @@ -145,6 +178,8 @@ unset(KERNELS CACHE) unset(USE_STATIC_KERNEL CACHE) unset(DIS_QUANT CACHE) unset(DIS_FLOAT CACHE) +unset(ENABLE_ONERT_MICRO_TEST CACHE) +unset(NOT_BUILD_EXTERNALS CACHE) set(MICRO_ARM_BINARY "${MICRO_ARM_BUILD_DIR}/luci-interpreter/src/libluci_interpreter_micro.a") diff --git a/onert-micro/README.md b/onert-micro/README.md index 6641ad7..062dbdb 100644 --- a/onert-micro/README.md +++ b/onert-micro/README.md @@ -1,10 +1,10 @@ -# luci-micro +# onert-micro -`luci-micro` is MCU specialized build of luci-interpreter with several benchmark applications. +`onert-micro`(a.k.a `luci-micro`) is MCU specialized build of luci-interpreter with several benchmark applications. ## Contents -Luci-micro contains cmake infrastructure to build: +onert-micro contains cmake infrastructure to build: - stand-alone interpreter library - benchmark applications using luci interpreter on arm MCUs @@ -29,18 +29,10 @@ $ sudo apt-get install gcc-arm-none-eabi $ cd $ mkdir build # cd build -$ cmake ../infra/nncc +$ cmake ../infra/onert-micro $ make -j$(nproc) luci_interpreter_micro_arm ``` -**nncc script build** - -``` bash -$ cd -$ ./nncc configure -$ ./nncc build -j$(nproc) luci_interpreter_micro_arm -``` - ### Known issues Interpreter uses TensorFlow headers that produces warnings. @@ -53,4 +45,84 @@ but some old arm compilers have issues with it: ## How to use -TBD +### Convert tflite model to circle model + +To inference with tflite model, you need to convert it to circle model format(https://github.com/Samsung/ONE/blob/master/res/CircleSchema/0.4/circle_schema.fbs). +Please refer to `tflite2circle` tool(https://github.com/Samsung/ONE/tree/master/compiler/tflite2circle) for this purpose. + +### Convert to c array model + +Many MCU platforms are lack of file system support. The proper way to provide a model to onert-micro is to convert it into c array so that it can be compiled into MCU binary. + +``` bash +xxi -i model.circle > model.h +``` + +Then, model.h looks like this: + +``` cpp +unsigned char model_circle[] = { + 0x22, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x0e, 0x00, + // ..... +}; +unsigned int model_circle_len = 1004; +``` + +### API + +Once you have c array model, you are ready to use onert-micro. + +To run a model with onert-micro, follow the instruction: + +1. Include onert-micro header + +``` cpp +#include +``` + +2. Create interpreter instance + +onert-micro interpreter expects model as c array as mentioned in [Previous Section](#convert-to-c-array-model). + +``` cpp +#include "model.h" + +luci_interpreter::Interpreter interpreter(model_circle, true); +``` + +3. Feed input data + +To feed input data into interpreter, we need to do two steps: 1) allocate input tensors and 2) copy input into input tensors. + +``` cpp + for (int32_t i = 0; i < num_inputs; i++) + { + auto input_data = reinterpret_cast(interpreter.allocateInputTensor(i)); + readDataFromFile(std::string(input_prefix) + std::to_string(i), input_data, + interpreter.getInputDataSizeByIndex(i)); + } +``` + +4. Do inference + +``` cpp + interpreter.interpret(); +``` + +5. Get output data + +``` cpp + auto data = interpreter.readOutputTensor(i); +``` + + +### Reduce Binary Size + +onert-micro provides compile flags to generate reduced-size binary. + +- `DIS_QUANT` : Flag for Disabling Quantized Type Operation +- `DIS_FLOAT` : Flag for Disabling Float Operation +- `DIS_DYN_SHAPES` : Flag for Disabling Dynamic Shape Support + +Also, you can build onert-micro library only with kernels in target models. +For this, please remove all the kernels from [KernelsToBuild.lst](./luci-interpreter/pal/mcu/KernelsToBuild.lst) except kernels in your target model. diff --git a/onert-micro/eval-driver/Driver.cpp b/onert-micro/eval-driver/Driver.cpp index 7ec4219..d29c41d 100644 --- a/onert-micro/eval-driver/Driver.cpp +++ b/onert-micro/eval-driver/Driver.cpp @@ -94,7 +94,7 @@ int entry(int argc, char **argv) } // Create interpreter. - luci_interpreter::Interpreter interpreter(model_data.data()); + luci_interpreter::Interpreter interpreter(model_data.data(), true); // Set input. // Data for n'th input is read from ${input_prefix}n diff --git a/onert-micro/externals/CMakeLists.txt b/onert-micro/externals/CMakeLists.txt new file mode 100644 index 0000000..221001c --- /dev/null +++ b/onert-micro/externals/CMakeLists.txt @@ -0,0 +1,9 @@ +unset(OUTPUT_FILES) +set(OUTPUT_FILES "${ONERT_MICRO_EXTERNAL_DIR}/gen/circle-generated/circle/schema_generated.h") +set(TGT luci_micro_circle_schema) + +# NOTE This header-only library +add_library(${TGT} STATIC ${OUTPUT_FILES}) +set_target_properties(${TGT} PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(${TGT} PUBLIC "${ONERT_MICRO_EXTERNAL_DIR}/gen") +target_include_directories(${TGT} PUBLIC "${ONERT_MICRO_EXTERNAL_DIR}") diff --git a/onert-micro/externals/flatbuffers/base.h b/onert-micro/externals/flatbuffers/base.h new file mode 100644 index 0000000..56bf9b4 --- /dev/null +++ b/onert-micro/externals/flatbuffers/base.h @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_BASE_H_ +#define FLATBUFFERS_BASE_H_ + +// clang-format off + +// If activate should be declared and included first. +#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \ + defined(_MSC_VER) && defined(_DEBUG) + // The _CRTDBG_MAP_ALLOC inside will replace + // calloc/free (etc) to its debug version using #define directives. + #define _CRTDBG_MAP_ALLOC + #include + #include + // Replace operator new by trace-enabled version. + #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new DEBUG_NEW +#endif + +#if !defined(FLATBUFFERS_ASSERT) +#include +#define FLATBUFFERS_ASSERT assert +#elif defined(FLATBUFFERS_ASSERT_INCLUDE) +// Include file with forward declaration +#include FLATBUFFERS_ASSERT_INCLUDE +#endif + +#ifndef ARDUINO +#include +#endif + +#include +#include +#include + +#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) + #include +#else + #include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__unix__) && !defined(FLATBUFFERS_LOCALE_INDEPENDENT) + #include +#endif + +#ifdef _STLPORT_VERSION + #define FLATBUFFERS_CPP98_STL +#endif + +#ifdef __ANDROID__ + #include +#endif + +#if defined(__ICCARM__) +#include +#endif + +// Note the __clang__ check is needed, because clang presents itself +// as an older GNUC compiler (4.2). +// Clang 3.3 and later implement all of the ISO C++ 2011 standard. +// Clang 3.4 and later implement all of the ISO C++ 2014 standard. +// http://clang.llvm.org/cxx_status.html + +// Note the MSVC value '__cplusplus' may be incorrect: +// The '__cplusplus' predefined macro in the MSVC stuck at the value 199711L, +// indicating (erroneously!) that the compiler conformed to the C++98 Standard. +// This value should be correct starting from MSVC2017-15.7-Preview-3. +// The '__cplusplus' will be valid only if MSVC2017-15.7-P3 and the `/Zc:__cplusplus` switch is set. +// Workaround (for details see MSDN): +// Use the _MSC_VER and _MSVC_LANG definition instead of the __cplusplus for compatibility. +// The _MSVC_LANG macro reports the Standard version regardless of the '/Zc:__cplusplus' switch. + +#if defined(__GNUC__) && !defined(__clang__) + #define FLATBUFFERS_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#else + #define FLATBUFFERS_GCC 0 +#endif + +#if defined(__clang__) + #define FLATBUFFERS_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#else + #define FLATBUFFERS_CLANG 0 +#endif + +/// @cond FLATBUFFERS_INTERNAL +#if __cplusplus <= 199711L && \ + (!defined(_MSC_VER) || _MSC_VER < 1600) && \ + (!defined(__GNUC__) || \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400)) + #error A C++11 compatible compiler with support for the auto typing is \ + required for FlatBuffers. + #error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ +#endif + +#if !defined(__clang__) && \ + defined(__GNUC__) && \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600) + // Backwards compatibility for g++ 4.4, and 4.5 which don't have the nullptr + // and constexpr keywords. Note the __clang__ check is needed, because clang + // presents itself as an older GNUC compiler. + #ifndef nullptr_t + const class nullptr_t { + public: + template inline operator T*() const { return 0; } + private: + void operator&() const; + } nullptr = {}; + #endif + #ifndef constexpr + #define constexpr const + #endif +#endif + +// The wire format uses a little endian encoding (since that's efficient for +// the common platforms). +#if defined(__s390x__) + #define FLATBUFFERS_LITTLEENDIAN 0 +#endif // __s390x__ +#if !defined(FLATBUFFERS_LITTLEENDIAN) + #if defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__) + #if (defined(__BIG_ENDIAN__) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + #define FLATBUFFERS_LITTLEENDIAN 0 + #else + #define FLATBUFFERS_LITTLEENDIAN 1 + #endif // __BIG_ENDIAN__ + #elif defined(_MSC_VER) + #if defined(_M_PPC) + #define FLATBUFFERS_LITTLEENDIAN 0 + #else + #define FLATBUFFERS_LITTLEENDIAN 1 + #endif + #else + #error Unable to determine endianness, define FLATBUFFERS_LITTLEENDIAN. + #endif +#endif // !defined(FLATBUFFERS_LITTLEENDIAN) + +#define FLATBUFFERS_VERSION_MAJOR 2 +#define FLATBUFFERS_VERSION_MINOR 0 +#define FLATBUFFERS_VERSION_REVISION 0 +#define FLATBUFFERS_STRING_EXPAND(X) #X +#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) +namespace flatbuffers { + // Returns version as string "MAJOR.MINOR.REVISION". + const char* FLATBUFFERS_VERSION(); +} + +#if (!defined(_MSC_VER) || _MSC_VER > 1600) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 407)) || \ + defined(__clang__) + #define FLATBUFFERS_FINAL_CLASS final + #define FLATBUFFERS_OVERRIDE override + #define FLATBUFFERS_EXPLICIT_CPP11 explicit + #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t +#else + #define FLATBUFFERS_FINAL_CLASS + #define FLATBUFFERS_OVERRIDE + #define FLATBUFFERS_EXPLICIT_CPP11 + #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 200704) + #define FLATBUFFERS_CONSTEXPR constexpr + #define FLATBUFFERS_CONSTEXPR_CPP11 constexpr + #define FLATBUFFERS_CONSTEXPR_DEFINED +#else + #define FLATBUFFERS_CONSTEXPR const + #define FLATBUFFERS_CONSTEXPR_CPP11 +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201402L) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) + #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11 +#else + #define FLATBUFFERS_CONSTEXPR_CPP14 +#endif + +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \ + (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023026)) || \ + defined(__clang__) + #define FLATBUFFERS_NOEXCEPT noexcept +#else + #define FLATBUFFERS_NOEXCEPT +#endif + +// NOTE: the FLATBUFFERS_DELETE_FUNC macro may change the access mode to +// private, so be sure to put it at the end or reset access mode explicitly. +#if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \ + defined(__clang__) + #define FLATBUFFERS_DELETE_FUNC(func) func = delete +#else + #define FLATBUFFERS_DELETE_FUNC(func) private: func +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \ + defined(__clang__) + #define FLATBUFFERS_DEFAULT_DECLARATION +#endif + +// Check if we can use template aliases +// Not possible if Microsoft Compiler before 2012 +// Possible is the language feature __cpp_alias_templates is defined well +// Or possible if the C++ std is C+11 or newer +#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \ + || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \ + || (defined(__cplusplus) && __cplusplus >= 201103L) + #define FLATBUFFERS_TEMPLATES_ALIASES +#endif + +#ifndef FLATBUFFERS_HAS_STRING_VIEW + // Only provide flatbuffers::string_view if __has_include can be used + // to detect a header that provides an implementation + #if defined(__has_include) + // Check for std::string_view (in c++17) + #if __has_include() && (__cplusplus >= 201606 || (defined(_HAS_CXX17) && _HAS_CXX17)) + #include + namespace flatbuffers { + typedef std::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + // Check for std::experimental::string_view (in c++14, compiler-dependent) + #elif __has_include() && (__cplusplus >= 201411) + #include + namespace flatbuffers { + typedef std::experimental::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + // Check for absl::string_view + #elif __has_include("absl/strings/string_view.h") + #include "absl/strings/string_view.h" + namespace flatbuffers { + typedef absl::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + #endif + #endif // __has_include +#endif // !FLATBUFFERS_HAS_STRING_VIEW + +#ifndef FLATBUFFERS_HAS_NEW_STRTOD + // Modern (C++11) strtod and strtof functions are available for use. + // 1) nan/inf strings as argument of strtod; + // 2) hex-float as argument of strtod/strtof. + #if (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \ + (defined(__clang__)) + #define FLATBUFFERS_HAS_NEW_STRTOD 1 + #endif +#endif // !FLATBUFFERS_HAS_NEW_STRTOD + +#ifndef FLATBUFFERS_LOCALE_INDEPENDENT + // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}. + #if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \ + (defined(_XOPEN_VERSION) && (_XOPEN_VERSION>=700)) && (!defined(__ANDROID_API__) || (defined(__ANDROID_API__) && (__ANDROID_API__>=21)))) + #define FLATBUFFERS_LOCALE_INDEPENDENT 1 + #else + #define FLATBUFFERS_LOCALE_INDEPENDENT 0 + #endif +#endif // !FLATBUFFERS_LOCALE_INDEPENDENT + +// Suppress Undefined Behavior Sanitizer (recoverable only). Usage: +// - __supress_ubsan__("undefined") +// - __supress_ubsan__("signed-integer-overflow") +#if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7)) + #define __supress_ubsan__(type) __attribute__((no_sanitize(type))) +#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409) + #define __supress_ubsan__(type) __attribute__((no_sanitize_undefined)) +#else + #define __supress_ubsan__(type) +#endif + +// This is constexpr function used for checking compile-time constants. +// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. +template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { + return !!t; +} + +// Enable C++ attribute [[]] if std:c++17 or higher. +#if ((__cplusplus >= 201703L) \ + || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))) + // All attributes unknown to an implementation are ignored without causing an error. + #define FLATBUFFERS_ATTRIBUTE(attr) [[attr]] + + #define FLATBUFFERS_FALLTHROUGH() [[fallthrough]] +#else + #define FLATBUFFERS_ATTRIBUTE(attr) + + #if FLATBUFFERS_CLANG >= 30800 + #define FLATBUFFERS_FALLTHROUGH() [[clang::fallthrough]] + #elif FLATBUFFERS_GCC >= 70300 + #define FLATBUFFERS_FALLTHROUGH() [[gnu::fallthrough]] + #else + #define FLATBUFFERS_FALLTHROUGH() + #endif +#endif + +/// @endcond + +/// @file +namespace flatbuffers { + +/// @cond FLATBUFFERS_INTERNAL +// Our default offset / size type, 32bit on purpose on 64bit systems. +// Also, using a consistent offset type maintains compatibility of serialized +// offset values between 32bit and 64bit systems. +typedef uint32_t uoffset_t; + +// Signed offsets for references that can go in both directions. +typedef int32_t soffset_t; + +// Offset/index used in v-tables, can be changed to uint8_t in +// format forks to save a bit of space if desired. +typedef uint16_t voffset_t; + +typedef uintmax_t largest_scalar_t; + +// In 32bits, this evaluates to 2GB - 1 +#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(::flatbuffers::soffset_t) * 8 - 1)) - 1) + +// We support aligning the contents of buffers up to this size. +#define FLATBUFFERS_MAX_ALIGNMENT 16 + +inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { + return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) && + (align & (align - 1)) == 0; // must be power of 2 +} + +#if defined(_MSC_VER) + #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized + #pragma warning(push) + #pragma warning(disable: 4127) // C4127: conditional expression is constant +#endif + +template T EndianSwap(T t) { + #if defined(_MSC_VER) + #define FLATBUFFERS_BYTESWAP16 _byteswap_ushort + #define FLATBUFFERS_BYTESWAP32 _byteswap_ulong + #define FLATBUFFERS_BYTESWAP64 _byteswap_uint64 + #elif defined(__ICCARM__) + #define FLATBUFFERS_BYTESWAP16 __REV16 + #define FLATBUFFERS_BYTESWAP32 __REV + #define FLATBUFFERS_BYTESWAP64(x) \ + ((__REV(static_cast(x >> 32U))) | (static_cast(__REV(static_cast(x)))) << 32U) + #else + #if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ < 408 && !defined(__clang__) + // __builtin_bswap16 was missing prior to GCC 4.8. + #define FLATBUFFERS_BYTESWAP16(x) \ + static_cast(__builtin_bswap32(static_cast(x) << 16)) + #else + #define FLATBUFFERS_BYTESWAP16 __builtin_bswap16 + #endif + #define FLATBUFFERS_BYTESWAP32 __builtin_bswap32 + #define FLATBUFFERS_BYTESWAP64 __builtin_bswap64 + #endif + if (sizeof(T) == 1) { // Compile-time if-then's. + return t; + } else if (sizeof(T) == 2) { + union { T t; uint16_t i; } u = { t }; + u.i = FLATBUFFERS_BYTESWAP16(u.i); + return u.t; + } else if (sizeof(T) == 4) { + union { T t; uint32_t i; } u = { t }; + u.i = FLATBUFFERS_BYTESWAP32(u.i); + return u.t; + } else if (sizeof(T) == 8) { + union { T t; uint64_t i; } u = { t }; + u.i = FLATBUFFERS_BYTESWAP64(u.i); + return u.t; + } else { + FLATBUFFERS_ASSERT(0); + return t; + } +} + +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + + +template T EndianScalar(T t) { + #if FLATBUFFERS_LITTLEENDIAN + return t; + #else + return EndianSwap(t); + #endif +} + +template +// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. +__supress_ubsan__("alignment") +T ReadScalar(const void *p) { + return EndianScalar(*reinterpret_cast(p)); +} + +// See https://github.com/google/flatbuffers/issues/5950 + +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + +template +// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. +__supress_ubsan__("alignment") +void WriteScalar(void *p, T t) { + *reinterpret_cast(p) = EndianScalar(t); +} + +template struct Offset; +template __supress_ubsan__("alignment") void WriteScalar(void *p, Offset t) { + *reinterpret_cast(p) = EndianScalar(t.o); +} + +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic pop +#endif + +// Computes how many bytes you'd have to pad to be able to write an +// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in +// memory). +__supress_ubsan__("unsigned-integer-overflow") +inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { + return ((~buf_size) + 1) & (scalar_size - 1); +} + +} // namespace flatbuffers +#endif // FLATBUFFERS_BASE_H_ diff --git a/onert-micro/externals/flatbuffers/code_generators.h b/onert-micro/externals/flatbuffers/code_generators.h new file mode 100644 index 0000000..3908ea5 --- /dev/null +++ b/onert-micro/externals/flatbuffers/code_generators.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_CODE_GENERATORS_H_ +#define FLATBUFFERS_CODE_GENERATORS_H_ + +#include +#include + +#include "flatbuffers/idl.h" + +namespace flatbuffers +{ + +// Utility class to assist in generating code through use of text templates. +// +// Example code: +// CodeWriter code("\t"); +// code.SetValue("NAME", "Foo"); +// code += "void {{NAME}}() { printf("%s", "{{NAME}}"); }"; +// code.SetValue("NAME", "Bar"); +// code += "void {{NAME}}() { printf("%s", "{{NAME}}"); }"; +// std::cout << code.ToString() << std::endl; +// +// Output: +// void Foo() { printf("%s", "Foo"); } +// void Bar() { printf("%s", "Bar"); } +class CodeWriter +{ +public: + CodeWriter(std::string pad = std::string()) : pad_(pad), cur_ident_lvl_(0), ignore_ident_(false) + { + } + + // Clears the current "written" code. + void Clear() + { + stream_.str(""); + stream_.clear(); + } + + // Associates a key with a value. All subsequent calls to operator+=, where + // the specified key is contained in {{ and }} delimiters will be replaced by + // the given value. + void SetValue(const std::string &key, const std::string &value) { value_map_[key] = value; } + + std::string GetValue(const std::string &key) const + { + const auto it = value_map_.find(key); + return it == value_map_.end() ? "" : it->second; + } + + // Appends the given text to the generated code as well as a newline + // character. Any text within {{ and }} delimiters is replaced by values + // previously stored in the CodeWriter by calling SetValue above. The newline + // will be suppressed if the text ends with the \\ character. + void operator+=(std::string text); + + // Returns the current contents of the CodeWriter as a std::string. + std::string ToString() const { return stream_.str(); } + + // Increase ident level for writing code + void IncrementIdentLevel() { cur_ident_lvl_++; } + // Decrease ident level for writing code + void DecrementIdentLevel() + { + if (cur_ident_lvl_) + cur_ident_lvl_--; + } + + void SetPadding(const std::string &padding) { pad_ = padding; } + +private: + std::map value_map_; + std::stringstream stream_; + std::string pad_; + int cur_ident_lvl_; + bool ignore_ident_; + + // Add ident padding (tab or space) based on ident level + void AppendIdent(std::stringstream &stream); +}; + +class BaseGenerator +{ +public: + virtual bool generate() = 0; + + static std::string NamespaceDir(const Parser &parser, const std::string &path, + const Namespace &ns, const bool dasherize = false); + + static std::string ToDasherizedCase(const std::string pascal_case); + + std::string GeneratedFileName(const std::string &path, const std::string &file_name, + const IDLOptions &options) const; + +protected: + BaseGenerator(const Parser &parser, const std::string &path, const std::string &file_name, + std::string qualifying_start, std::string qualifying_separator, + std::string default_extension) + : parser_(parser), path_(path), file_name_(file_name), qualifying_start_(qualifying_start), + qualifying_separator_(qualifying_separator), default_extension_(default_extension) + { + } + virtual ~BaseGenerator() {} + + // No copy/assign. + BaseGenerator &operator=(const BaseGenerator &); + BaseGenerator(const BaseGenerator &); + + std::string NamespaceDir(const Namespace &ns, const bool dasherize = false) const; + + static const char *FlatBuffersGeneratedWarning(); + + static std::string FullNamespace(const char *separator, const Namespace &ns); + + static std::string LastNamespacePart(const Namespace &ns); + + // tracks the current namespace for early exit in WrapInNameSpace + // c++, java and csharp returns a different namespace from + // the following default (no early exit, always fully qualify), + // which works for js and php + virtual const Namespace *CurrentNameSpace() const { return nullptr; } + + // Ensure that a type is prefixed with its namespace even within + // its own namespace to avoid conflict between generated method + // names and similarly named classes or structs + std::string WrapInNameSpace(const Namespace *ns, const std::string &name) const; + + std::string WrapInNameSpace(const Definition &def) const; + + std::string GetNameSpace(const Definition &def) const; + + const Parser &parser_; + const std::string &path_; + const std::string &file_name_; + const std::string qualifying_start_; + const std::string qualifying_separator_; + const std::string default_extension_; +}; + +struct CommentConfig +{ + const char *first_line; + const char *content_line_prefix; + const char *last_line; +}; + +extern void GenComment(const std::vector &dc, std::string *code_ptr, + const CommentConfig *config, const char *prefix = ""); + +class FloatConstantGenerator +{ +public: + virtual ~FloatConstantGenerator() {} + std::string GenFloatConstant(const FieldDef &field) const; + +private: + virtual std::string Value(double v, const std::string &src) const = 0; + virtual std::string Inf(double v) const = 0; + virtual std::string NaN(double v) const = 0; + + virtual std::string Value(float v, const std::string &src) const = 0; + virtual std::string Inf(float v) const = 0; + virtual std::string NaN(float v) const = 0; + + template std::string GenFloatConstantImpl(const FieldDef &field) const; +}; + +class SimpleFloatConstantGenerator : public FloatConstantGenerator +{ +public: + SimpleFloatConstantGenerator(const char *nan_number, const char *pos_inf_number, + const char *neg_inf_number); + +private: + std::string Value(double v, const std::string &src) const FLATBUFFERS_OVERRIDE; + std::string Inf(double v) const FLATBUFFERS_OVERRIDE; + std::string NaN(double v) const FLATBUFFERS_OVERRIDE; + + std::string Value(float v, const std::string &src) const FLATBUFFERS_OVERRIDE; + std::string Inf(float v) const FLATBUFFERS_OVERRIDE; + std::string NaN(float v) const FLATBUFFERS_OVERRIDE; + + const std::string nan_number_; + const std::string pos_inf_number_; + const std::string neg_inf_number_; +}; + +// C++, C#, Java like generator. +class TypedFloatConstantGenerator : public FloatConstantGenerator +{ +public: + TypedFloatConstantGenerator(const char *double_prefix, const char *single_prefix, + const char *nan_number, const char *pos_inf_number, + const char *neg_inf_number = ""); + +private: + std::string Value(double v, const std::string &src) const FLATBUFFERS_OVERRIDE; + std::string Inf(double v) const FLATBUFFERS_OVERRIDE; + + std::string NaN(double v) const FLATBUFFERS_OVERRIDE; + + std::string Value(float v, const std::string &src) const FLATBUFFERS_OVERRIDE; + std::string Inf(float v) const FLATBUFFERS_OVERRIDE; + std::string NaN(float v) const FLATBUFFERS_OVERRIDE; + + std::string MakeNaN(const std::string &prefix) const; + std::string MakeInf(bool neg, const std::string &prefix) const; + + const std::string double_prefix_; + const std::string single_prefix_; + const std::string nan_number_; + const std::string pos_inf_number_; + const std::string neg_inf_number_; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_CODE_GENERATORS_H_ diff --git a/onert-micro/externals/flatbuffers/flatbuffers.h b/onert-micro/externals/flatbuffers/flatbuffers.h new file mode 100644 index 0000000..3005d89 --- /dev/null +++ b/onert-micro/externals/flatbuffers/flatbuffers.h @@ -0,0 +1,3078 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_H_ +#define FLATBUFFERS_H_ + +#include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" + +#ifndef FLATBUFFERS_CPP98_STL +#include +#endif + +#if defined(FLATBUFFERS_NAN_DEFAULTS) +#include +#endif + +namespace flatbuffers +{ +// Generic 'operator==' with conditional specialisations. +// T e - new value of a scalar field. +// T def - default of scalar (is known at compile-time). +template inline bool IsTheSameAs(T e, T def) { return e == def; } + +#if defined(FLATBUFFERS_NAN_DEFAULTS) && defined(FLATBUFFERS_HAS_NEW_STRTOD) && \ + (FLATBUFFERS_HAS_NEW_STRTOD > 0) +// Like `operator==(e, def)` with weak NaN if T=(float|double). +template inline bool IsFloatTheSameAs(T e, T def) +{ + return (e == def) || ((def != def) && (e != e)); +} +template <> inline bool IsTheSameAs(float e, float def) { return IsFloatTheSameAs(e, def); } +template <> inline bool IsTheSameAs(double e, double def) +{ + return IsFloatTheSameAs(e, def); +} +#endif + +// Check 'v' is out of closed range [low; high]. +// Workaround for GCC warning [-Werror=type-limits]: +// comparison is always true due to limited range of data type. +template inline bool IsOutRange(const T &v, const T &low, const T &high) +{ + return (v < low) || (high < v); +} + +// Check 'v' is in closed range [low; high]. +template inline bool IsInRange(const T &v, const T &low, const T &high) +{ + return !IsOutRange(v, low, high); +} + +// Wrapper for uoffset_t to allow safe template specialization. +// Value is allowed to be 0 to indicate a null object (see e.g. AddOffset). +template struct Offset +{ + uoffset_t o; + Offset() : o(0) {} + Offset(uoffset_t _o) : o(_o) {} + Offset Union() const { return Offset(o); } + bool IsNull() const { return !o; } +}; + +inline void EndianCheck() +{ + int endiantest = 1; + // If this fails, see FLATBUFFERS_LITTLEENDIAN above. + FLATBUFFERS_ASSERT(*reinterpret_cast(&endiantest) == FLATBUFFERS_LITTLEENDIAN); + (void)endiantest; +} + +template FLATBUFFERS_CONSTEXPR size_t AlignOf() +{ +#ifdef _MSC_VER + return __alignof(T); +#else +#ifndef alignof + return __alignof__(T); +#else + return alignof(T); +#endif +#endif + // clang-format on +} + +// When we read serialized data from memory, in the case of most scalars, +// we want to just read T, but in the case of Offset, we want to actually +// perform the indirection and return a pointer. +// The template specialization below does just that. +// It is wrapped in a struct since function templates can't overload on the +// return type like this. +// The typedef is for the convenience of callers of this function +// (avoiding the need for a trailing return decltype) +template struct IndirectHelper +{ + typedef T return_type; + typedef T mutable_return_type; + static const size_t element_stride = sizeof(T); + static return_type Read(const uint8_t *p, uoffset_t i) + { + return EndianScalar((reinterpret_cast(p))[i]); + } +}; +template struct IndirectHelper> +{ + typedef const T *return_type; + typedef T *mutable_return_type; + static const size_t element_stride = sizeof(uoffset_t); + static return_type Read(const uint8_t *p, uoffset_t i) + { + p += i * sizeof(uoffset_t); + return reinterpret_cast(p + ReadScalar(p)); + } +}; +template struct IndirectHelper +{ + typedef const T *return_type; + typedef T *mutable_return_type; + static const size_t element_stride = sizeof(T); + static return_type Read(const uint8_t *p, uoffset_t i) + { + return reinterpret_cast(p + i * sizeof(T)); + } +}; + +// An STL compatible iterator implementation for Vector below, effectively +// calling Get() for every element. +template struct VectorIterator +{ + typedef std::random_access_iterator_tag iterator_category; + typedef IT value_type; + typedef ptrdiff_t difference_type; + typedef IT *pointer; + typedef IT &reference; + + VectorIterator(const uint8_t *data, uoffset_t i) + : data_(data + IndirectHelper::element_stride * i) + { + } + VectorIterator(const VectorIterator &other) : data_(other.data_) {} + VectorIterator() : data_(nullptr) {} + + VectorIterator &operator=(const VectorIterator &other) + { + data_ = other.data_; + return *this; + } + +#if !defined(FLATBUFFERS_CPP98_STL) + VectorIterator &operator=(VectorIterator &&other) + { + data_ = other.data_; + return *this; + } +#endif // !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + + bool operator==(const VectorIterator &other) const { return data_ == other.data_; } + + bool operator<(const VectorIterator &other) const { return data_ < other.data_; } + + bool operator!=(const VectorIterator &other) const { return data_ != other.data_; } + + difference_type operator-(const VectorIterator &other) const + { + return (data_ - other.data_) / IndirectHelper::element_stride; + } + + // Note: return type is incompatible with the standard + // `reference operator*()`. + IT operator*() const { return IndirectHelper::Read(data_, 0); } + + // Note: return type is incompatible with the standard + // `pointer operator->()`. + IT operator->() const { return IndirectHelper::Read(data_, 0); } + + VectorIterator &operator++() + { + data_ += IndirectHelper::element_stride; + return *this; + } + + VectorIterator operator++(int) + { + VectorIterator temp(data_, 0); + data_ += IndirectHelper::element_stride; + return temp; + } + + VectorIterator operator+(const uoffset_t &offset) const + { + return VectorIterator(data_ + offset * IndirectHelper::element_stride, 0); + } + + VectorIterator &operator+=(const uoffset_t &offset) + { + data_ += offset * IndirectHelper::element_stride; + return *this; + } + + VectorIterator &operator--() + { + data_ -= IndirectHelper::element_stride; + return *this; + } + + VectorIterator operator--(int) + { + VectorIterator temp(data_, 0); + data_ -= IndirectHelper::element_stride; + return temp; + } + + VectorIterator operator-(const uoffset_t &offset) const + { + return VectorIterator(data_ - offset * IndirectHelper::element_stride, 0); + } + + VectorIterator &operator-=(const uoffset_t &offset) + { + data_ -= offset * IndirectHelper::element_stride; + return *this; + } + +private: + const uint8_t *data_; +}; + +template struct VectorReverseIterator : public std::reverse_iterator +{ + explicit VectorReverseIterator(Iterator iter) : std::reverse_iterator(iter) {} + + // Note: return type is incompatible with the standard + // `reference operator*()`. + typename Iterator::value_type operator*() const + { + auto tmp = std::reverse_iterator::current; + return *--tmp; + } + + // Note: return type is incompatible with the standard + // `pointer operator->()`. + typename Iterator::value_type operator->() const + { + auto tmp = std::reverse_iterator::current; + return *--tmp; + } +}; + +struct String; + +// This is used as a helper type for accessing vectors. +// Vector::data() assumes the vector elements start after the length field. +template class Vector +{ +public: + typedef VectorIterator::mutable_return_type> iterator; + typedef VectorIterator::return_type> const_iterator; + typedef VectorReverseIterator reverse_iterator; + typedef VectorReverseIterator const_reverse_iterator; + + uoffset_t size() const { return EndianScalar(length_); } + + // Deprecated: use size(). Here for backwards compatibility. + FLATBUFFERS_ATTRIBUTE(deprecated("use size() instead")) + uoffset_t Length() const { return size(); } + + typedef typename IndirectHelper::return_type return_type; + typedef typename IndirectHelper::mutable_return_type mutable_return_type; + typedef return_type value_type; + + return_type Get(uoffset_t i) const + { + FLATBUFFERS_ASSERT(i < size()); + return IndirectHelper::Read(Data(), i); + } + + return_type operator[](uoffset_t i) const { return Get(i); } + + // If this is a Vector of enums, T will be its storage type, not the enum + // type. This function makes it convenient to retrieve value with enum + // type E. + template E GetEnum(uoffset_t i) const { return static_cast(Get(i)); } + + // If this a vector of unions, this does the cast for you. There's no check + // to make sure this is the right type! + template const U *GetAs(uoffset_t i) const + { + return reinterpret_cast(Get(i)); + } + + // If this a vector of unions, this does the cast for you. There's no check + // to make sure this is actually a string! + const String *GetAsString(uoffset_t i) const { return reinterpret_cast(Get(i)); } + + const void *GetStructFromOffset(size_t o) const + { + return reinterpret_cast(Data() + o); + } + + iterator begin() { return iterator(Data(), 0); } + const_iterator begin() const { return const_iterator(Data(), 0); } + + iterator end() { return iterator(Data(), size()); } + const_iterator end() const { return const_iterator(Data(), size()); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + const_iterator cbegin() const { return begin(); } + + const_iterator cend() const { return end(); } + + const_reverse_iterator crbegin() const { return rbegin(); } + + const_reverse_iterator crend() const { return rend(); } + + // Change elements if you have a non-const pointer to this object. + // Scalars only. See reflection.h, and the documentation. + void Mutate(uoffset_t i, const T &val) + { + FLATBUFFERS_ASSERT(i < size()); + WriteScalar(data() + i, val); + } + + // Change an element of a vector of tables (or strings). + // "val" points to the new table/string, as you can obtain from + // e.g. reflection::AddFlatBuffer(). + void MutateOffset(uoffset_t i, const uint8_t *val) + { + FLATBUFFERS_ASSERT(i < size()); + static_assert(sizeof(T) == sizeof(uoffset_t), "Unrelated types"); + WriteScalar(data() + i, static_cast(val - (Data() + i * sizeof(uoffset_t)))); + } + + // Get a mutable pointer to tables/strings inside this vector. + mutable_return_type GetMutableObject(uoffset_t i) const + { + FLATBUFFERS_ASSERT(i < size()); + return const_cast(IndirectHelper::Read(Data(), i)); + } + + // The raw data in little endian format. Use with care. + const uint8_t *Data() const { return reinterpret_cast(&length_ + 1); } + + uint8_t *Data() { return reinterpret_cast(&length_ + 1); } + + // Similarly, but typed, much like std::vector::data + const T *data() const { return reinterpret_cast(Data()); } + T *data() { return reinterpret_cast(Data()); } + + template return_type LookupByKey(K key) const + { + void *search_result = + std::bsearch(&key, Data(), size(), IndirectHelper::element_stride, KeyCompare); + + if (!search_result) + { + return nullptr; // Key not found. + } + + const uint8_t *element = reinterpret_cast(search_result); + + return IndirectHelper::Read(element, 0); + } + +protected: + // This class is only used to access pre-existing data. Don't ever + // try to construct these manually. + Vector(); + + uoffset_t length_; + +private: + // This class is a pointer. Copying will therefore create an invalid object. + // Private and unimplemented copy constructor. + Vector(const Vector &); + Vector &operator=(const Vector &); + + template static int KeyCompare(const void *ap, const void *bp) + { + const K *key = reinterpret_cast(ap); + const uint8_t *data = reinterpret_cast(bp); + auto table = IndirectHelper::Read(data, 0); + + // std::bsearch compares with the operands transposed, so we negate the + // result here. + return -table->KeyCompareWithValue(*key); + } +}; + +// Represent a vector much like the template above, but in this case we +// don't know what the element types are (used with reflection.h). +class VectorOfAny +{ +public: + uoffset_t size() const { return EndianScalar(length_); } + + const uint8_t *Data() const { return reinterpret_cast(&length_ + 1); } + uint8_t *Data() { return reinterpret_cast(&length_ + 1); } + +protected: + VectorOfAny(); + + uoffset_t length_; + +private: + VectorOfAny(const VectorOfAny &); + VectorOfAny &operator=(const VectorOfAny &); +}; + +#ifndef FLATBUFFERS_CPP98_STL +template Vector> *VectorCast(Vector> *ptr) +{ + static_assert(std::is_base_of::value, "Unrelated types"); + return reinterpret_cast> *>(ptr); +} + +template const Vector> *VectorCast(const Vector> *ptr) +{ + static_assert(std::is_base_of::value, "Unrelated types"); + return reinterpret_cast> *>(ptr); +} +#endif + +// Convenient helper function to get the length of any vector, regardless +// of whether it is null or not (the field is not set). +template static inline size_t VectorLength(const Vector *v) +{ + return v ? v->size() : 0; +} + +// This is used as a helper type for accessing arrays. +template class Array +{ + typedef typename flatbuffers::integral_constant::value> + scalar_tag; + typedef + typename flatbuffers::conditional::type IndirectHelperType; + +public: + typedef uint16_t size_type; + typedef typename IndirectHelper::return_type return_type; + typedef VectorIterator const_iterator; + typedef VectorReverseIterator const_reverse_iterator; + + FLATBUFFERS_CONSTEXPR uint16_t size() const { return length; } + + return_type Get(uoffset_t i) const + { + FLATBUFFERS_ASSERT(i < size()); + return IndirectHelper::Read(Data(), i); + } + + return_type operator[](uoffset_t i) const { return Get(i); } + + // If this is a Vector of enums, T will be its storage type, not the enum + // type. This function makes it convenient to retrieve value with enum + // type E. + template E GetEnum(uoffset_t i) const { return static_cast(Get(i)); } + + const_iterator begin() const { return const_iterator(Data(), 0); } + const_iterator end() const { return const_iterator(Data(), size()); } + + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + const_reverse_iterator crbegin() const { return rbegin(); } + const_reverse_iterator crend() const { return rend(); } + + // Get a mutable pointer to elements inside this array. + // This method used to mutate arrays of structs followed by a @p Mutate + // operation. For primitive types use @p Mutate directly. + // @warning Assignments and reads to/from the dereferenced pointer are not + // automatically converted to the correct endianness. + typename flatbuffers::conditional::type + GetMutablePointer(uoffset_t i) const + { + FLATBUFFERS_ASSERT(i < size()); + return const_cast(&data()[i]); + } + + // Change elements if you have a non-const pointer to this object. + void Mutate(uoffset_t i, const T &val) { MutateImpl(scalar_tag(), i, val); } + + // The raw data in little endian format. Use with care. + const uint8_t *Data() const { return data_; } + + uint8_t *Data() { return data_; } + + // Similarly, but typed, much like std::vector::data + const T *data() const { return reinterpret_cast(Data()); } + T *data() { return reinterpret_cast(Data()); } + + // Copy data from a span with endian conversion. + // If this Array and the span overlap, the behavior is undefined. + void CopyFromSpan(flatbuffers::span src) + { + const auto p1 = reinterpret_cast(src.data()); + const auto p2 = Data(); + FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) && !(p2 >= p1 && p2 < (p1 + length))); + (void)p1; + (void)p2; + + CopyFromSpanImpl(flatbuffers::integral_constant < bool, + !scalar_tag::value || sizeof(T) == 1 || FLATBUFFERS_LITTLEENDIAN > (), src); + } + +protected: + void MutateImpl(flatbuffers::integral_constant, uoffset_t i, const T &val) + { + FLATBUFFERS_ASSERT(i < size()); + WriteScalar(data() + i, val); + } + + void MutateImpl(flatbuffers::integral_constant, uoffset_t i, const T &val) + { + *(GetMutablePointer(i)) = val; + } + + void CopyFromSpanImpl(flatbuffers::integral_constant, + flatbuffers::span src) + { + // Use std::memcpy() instead of std::copy() to avoid preformance degradation + // due to aliasing if T is char or unsigned char. + // The size is known at compile time, so memcpy would be inlined. + std::memcpy(data(), src.data(), length * sizeof(T)); + } + + // Copy data from flatbuffers::span with endian conversion. + void CopyFromSpanImpl(flatbuffers::integral_constant, + flatbuffers::span src) + { + for (size_type k = 0; k < length; k++) + { + Mutate(k, src[k]); + } + } + + // This class is only used to access pre-existing data. Don't ever + // try to construct these manually. + // 'constexpr' allows us to use 'size()' at compile time. + // @note Must not use 'FLATBUFFERS_CONSTEXPR' here, as const is not allowed on + // a constructor. +#if defined(__cpp_constexpr) + constexpr Array(); +#else + Array(); +#endif + + uint8_t data_[length * sizeof(T)]; + +private: + // This class is a pointer. Copying will therefore create an invalid object. + // Private and unimplemented copy constructor. + Array(const Array &); + Array &operator=(const Array &); +}; + +// Specialization for Array[struct] with access using Offset pointer. +// This specialization used by idl_gen_text.cpp. +template class Array, length> +{ + static_assert(flatbuffers::is_same::value, "unexpected type T"); + +public: + typedef const void *return_type; + + const uint8_t *Data() const { return data_; } + + // Make idl_gen_text.cpp::PrintContainer happy. + return_type operator[](uoffset_t) const + { + FLATBUFFERS_ASSERT(false); + return nullptr; + } + +private: + // This class is only used to access pre-existing data. + Array(); + Array(const Array &); + Array &operator=(const Array &); + + uint8_t data_[1]; +}; + +// Cast a raw T[length] to a raw flatbuffers::Array +// without endian conversion. Use with care. +template Array &CastToArray(T (&arr)[length]) +{ + return *reinterpret_cast *>(arr); +} + +template const Array &CastToArray(const T (&arr)[length]) +{ + return *reinterpret_cast *>(arr); +} + +template +Array &CastToArrayOfEnum(T (&arr)[length]) +{ + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + +template +const Array &CastToArrayOfEnum(const T (&arr)[length]) +{ + static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); + return *reinterpret_cast *>(arr); +} + +// Lexicographically compare two strings (possibly containing nulls), and +// return true if the first is less than the second. +static inline bool StringLessThan(const char *a_data, uoffset_t a_size, const char *b_data, + uoffset_t b_size) +{ + const auto cmp = memcmp(a_data, b_data, (std::min)(a_size, b_size)); + return cmp == 0 ? a_size < b_size : cmp < 0; +} + +struct String : public Vector +{ + const char *c_str() const { return reinterpret_cast(Data()); } + std::string str() const { return std::string(c_str(), size()); } + +#ifdef FLATBUFFERS_HAS_STRING_VIEW + flatbuffers::string_view string_view() const { return flatbuffers::string_view(c_str(), size()); } +#endif // FLATBUFFERS_HAS_STRING_VIEW + // clang-format on + + bool operator<(const String &o) const + { + return StringLessThan(this->data(), this->size(), o.data(), o.size()); + } +}; + +// Convenience function to get std::string from a String returning an empty +// string on null pointer. +static inline std::string GetString(const String *str) { return str ? str->str() : ""; } + +// Convenience function to get char* from a String returning an empty string on +// null pointer. +static inline const char *GetCstring(const String *str) { return str ? str->c_str() : ""; } + +#ifdef FLATBUFFERS_HAS_STRING_VIEW +// Convenience function to get string_view from a String returning an empty +// string_view on null pointer. +static inline flatbuffers::string_view GetStringView(const String *str) +{ + return str ? str->string_view() : flatbuffers::string_view(); +} +#endif // FLATBUFFERS_HAS_STRING_VIEW + +// Allocator interface. This is flatbuffers-specific and meant only for +// `vector_downward` usage. +class Allocator +{ +public: + virtual ~Allocator() {} + + // Allocate `size` bytes of memory. + virtual uint8_t *allocate(size_t size) = 0; + + // Deallocate `size` bytes of memory at `p` allocated by this allocator. + virtual void deallocate(uint8_t *p, size_t size) = 0; + + // Reallocate `new_size` bytes of memory, replacing the old region of size + // `old_size` at `p`. In contrast to a normal realloc, this grows downwards, + // and is intended specifcally for `vector_downward` use. + // `in_use_back` and `in_use_front` indicate how much of `old_size` is + // actually in use at each end, and needs to be copied. + virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, size_t new_size, + size_t in_use_back, size_t in_use_front) + { + FLATBUFFERS_ASSERT(new_size > old_size); // vector_downward only grows + uint8_t *new_p = allocate(new_size); + memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, in_use_front); + deallocate(old_p, old_size); + return new_p; + } + +protected: + // Called by `reallocate_downward` to copy memory from `old_p` of `old_size` + // to `new_p` of `new_size`. Only memory of size `in_use_front` and + // `in_use_back` will be copied from the front and back of the old memory + // allocation. + void memcpy_downward(uint8_t *old_p, size_t old_size, uint8_t *new_p, size_t new_size, + size_t in_use_back, size_t in_use_front) + { + memcpy(new_p + new_size - in_use_back, old_p + old_size - in_use_back, in_use_back); + memcpy(new_p, old_p, in_use_front); + } +}; + +// DefaultAllocator uses new/delete to allocate memory regions +class DefaultAllocator : public Allocator +{ +public: + uint8_t *allocate(size_t size) FLATBUFFERS_OVERRIDE { return new uint8_t[size]; } + + void deallocate(uint8_t *p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } + + static void dealloc(void *p, size_t) { delete[] static_cast(p); } +}; + +// These functions allow for a null allocator to mean use the default allocator, +// as used by DetachedBuffer and vector_downward below. +// This is to avoid having a statically or dynamically allocated default +// allocator, or having to move it between the classes that may own it. +inline uint8_t *Allocate(Allocator *allocator, size_t size) +{ + return allocator ? allocator->allocate(size) : DefaultAllocator().allocate(size); +} + +inline void Deallocate(Allocator *allocator, uint8_t *p, size_t size) +{ + if (allocator) + allocator->deallocate(p, size); + else + DefaultAllocator().deallocate(p, size); +} + +inline uint8_t *ReallocateDownward(Allocator *allocator, uint8_t *old_p, size_t old_size, + size_t new_size, size_t in_use_back, size_t in_use_front) +{ + return allocator + ? allocator->reallocate_downward(old_p, old_size, new_size, in_use_back, in_use_front) + : DefaultAllocator().reallocate_downward(old_p, old_size, new_size, in_use_back, + in_use_front); +} + +// DetachedBuffer is a finished flatbuffer memory region, detached from its +// builder. The original memory region and allocator are also stored so that +// the DetachedBuffer can manage the memory lifetime. +class DetachedBuffer +{ +public: + DetachedBuffer() + : allocator_(nullptr), own_allocator_(false), buf_(nullptr), reserved_(0), cur_(nullptr), + size_(0) + { + } + + DetachedBuffer(Allocator *allocator, bool own_allocator, uint8_t *buf, size_t reserved, + uint8_t *cur, size_t sz) + : allocator_(allocator), own_allocator_(own_allocator), buf_(buf), reserved_(reserved), + cur_(cur), size_(sz) + { + } + +#if !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + DetachedBuffer(DetachedBuffer &&other) + : allocator_(other.allocator_), own_allocator_(other.own_allocator_), buf_(other.buf_), + reserved_(other.reserved_), cur_(other.cur_), size_(other.size_) + { + other.reset(); + } +#endif // !defined(FLATBUFFERS_CPP98_STL) + +#if !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + DetachedBuffer &operator=(DetachedBuffer &&other) + { + if (this == &other) + return *this; + + destroy(); + + allocator_ = other.allocator_; + own_allocator_ = other.own_allocator_; + buf_ = other.buf_; + reserved_ = other.reserved_; + cur_ = other.cur_; + size_ = other.size_; + + other.reset(); + + return *this; + } +#endif // !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + + ~DetachedBuffer() { destroy(); } + + const uint8_t *data() const { return cur_; } + + uint8_t *data() { return cur_; } + + size_t size() const { return size_; } + +#if 0 // disabled for now due to the ordering of classes in this header + template + bool Verify() const { + Verifier verifier(data(), size()); + return verifier.Verify(nullptr); + } + + template + const T* GetRoot() const { + return flatbuffers::GetRoot(data()); + } + + template + T* GetRoot() { + return flatbuffers::GetRoot(data()); + } +#endif + +#if !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + // These may change access mode, leave these at end of public section + FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)); + FLATBUFFERS_DELETE_FUNC(DetachedBuffer &operator=(const DetachedBuffer &other)); +#endif // !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + +protected: + Allocator *allocator_; + bool own_allocator_; + uint8_t *buf_; + size_t reserved_; + uint8_t *cur_; + size_t size_; + + inline void destroy() + { + if (buf_) + Deallocate(allocator_, buf_, reserved_); + if (own_allocator_ && allocator_) + { + delete allocator_; + } + reset(); + } + + inline void reset() + { + allocator_ = nullptr; + own_allocator_ = false; + buf_ = nullptr; + reserved_ = 0; + cur_ = nullptr; + size_ = 0; + } +}; + +// This is a minimal replication of std::vector functionality, +// except growing from higher to lower addresses. i.e push_back() inserts data +// in the lowest address in the vector. +// Since this vector leaves the lower part unused, we support a "scratch-pad" +// that can be stored there for temporary data, to share the allocated space. +// Essentially, this supports 2 std::vectors in a single buffer. +class vector_downward +{ +public: + explicit vector_downward(size_t initial_size, Allocator *allocator, bool own_allocator, + size_t buffer_minalign) + : allocator_(allocator), own_allocator_(own_allocator), initial_size_(initial_size), + buffer_minalign_(buffer_minalign), reserved_(0), buf_(nullptr), cur_(nullptr), + scratch_(nullptr) + { + } + +#if !defined(FLATBUFFERS_CPP98_STL) + vector_downward(vector_downward &&other) +#else + vector_downward(vector_downward &other) +#endif // defined(FLATBUFFERS_CPP98_STL) + // clang-format on + : allocator_(other.allocator_), own_allocator_(other.own_allocator_), + initial_size_(other.initial_size_), buffer_minalign_(other.buffer_minalign_), + reserved_(other.reserved_), buf_(other.buf_), cur_(other.cur_), scratch_(other.scratch_) + { + // No change in other.allocator_ + // No change in other.initial_size_ + // No change in other.buffer_minalign_ + other.own_allocator_ = false; + other.reserved_ = 0; + other.buf_ = nullptr; + other.cur_ = nullptr; + other.scratch_ = nullptr; + } + +#if !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + vector_downward &operator=(vector_downward &&other) + { + // Move construct a temporary and swap idiom + vector_downward temp(std::move(other)); + swap(temp); + return *this; + } +#endif // defined(FLATBUFFERS_CPP98_STL) + // clang-format on + + ~vector_downward() + { + clear_buffer(); + clear_allocator(); + } + + void reset() + { + clear_buffer(); + clear(); + } + + void clear() + { + if (buf_) + { + cur_ = buf_ + reserved_; + } + else + { + reserved_ = 0; + cur_ = nullptr; + } + clear_scratch(); + } + + void clear_scratch() { scratch_ = buf_; } + + void clear_allocator() + { + if (own_allocator_ && allocator_) + { + delete allocator_; + } + allocator_ = nullptr; + own_allocator_ = false; + } + + void clear_buffer() + { + if (buf_) + Deallocate(allocator_, buf_, reserved_); + buf_ = nullptr; + } + + // Relinquish the pointer to the caller. + uint8_t *release_raw(size_t &allocated_bytes, size_t &offset) + { + auto *buf = buf_; + allocated_bytes = reserved_; + offset = static_cast(cur_ - buf_); + + // release_raw only relinquishes the buffer ownership. + // Does not deallocate or reset the allocator. Destructor will do that. + buf_ = nullptr; + clear(); + return buf; + } + + // Relinquish the pointer to the caller. + DetachedBuffer release() + { + // allocator ownership (if any) is transferred to DetachedBuffer. + DetachedBuffer fb(allocator_, own_allocator_, buf_, reserved_, cur_, size()); + if (own_allocator_) + { + allocator_ = nullptr; + own_allocator_ = false; + } + buf_ = nullptr; + clear(); + return fb; + } + + size_t ensure_space(size_t len) + { + FLATBUFFERS_ASSERT(cur_ >= scratch_ && scratch_ >= buf_); + if (len > static_cast(cur_ - scratch_)) + { + reallocate(len); + } + // Beyond this, signed offsets may not have enough range: + // (FlatBuffers > 2GB not supported). + FLATBUFFERS_ASSERT(size() < FLATBUFFERS_MAX_BUFFER_SIZE); + return len; + } + + inline uint8_t *make_space(size_t len) + { + size_t space = ensure_space(len); + cur_ -= space; + return cur_; + } + + // Returns nullptr if using the DefaultAllocator. + Allocator *get_custom_allocator() { return allocator_; } + + uoffset_t size() const + { + return static_cast(reserved_ - static_cast(cur_ - buf_)); + } + + uoffset_t scratch_size() const { return static_cast(scratch_ - buf_); } + + size_t capacity() const { return reserved_; } + + uint8_t *data() const + { + FLATBUFFERS_ASSERT(cur_); + return cur_; + } + + uint8_t *scratch_data() const + { + FLATBUFFERS_ASSERT(buf_); + return buf_; + } + + uint8_t *scratch_end() const + { + FLATBUFFERS_ASSERT(scratch_); + return scratch_; + } + + uint8_t *data_at(size_t offset) const { return buf_ + reserved_ - offset; } + + void push(const uint8_t *bytes, size_t num) + { + if (num > 0) + { + memcpy(make_space(num), bytes, num); + } + } + + // Specialized version of push() that avoids memcpy call for small data. + template void push_small(const T &little_endian_t) + { + make_space(sizeof(T)); + *reinterpret_cast(cur_) = little_endian_t; + } + + template void scratch_push_small(const T &t) + { + ensure_space(sizeof(T)); + *reinterpret_cast(scratch_) = t; + scratch_ += sizeof(T); + } + + // fill() is most frequently called with small byte counts (<= 4), + // which is why we're using loops rather than calling memset. + void fill(size_t zero_pad_bytes) + { + make_space(zero_pad_bytes); + for (size_t i = 0; i < zero_pad_bytes; i++) + cur_[i] = 0; + } + + // Version for when we know the size is larger. + // Precondition: zero_pad_bytes > 0 + void fill_big(size_t zero_pad_bytes) { memset(make_space(zero_pad_bytes), 0, zero_pad_bytes); } + + void pop(size_t bytes_to_remove) { cur_ += bytes_to_remove; } + void scratch_pop(size_t bytes_to_remove) { scratch_ -= bytes_to_remove; } + + void swap(vector_downward &other) + { + using std::swap; + swap(allocator_, other.allocator_); + swap(own_allocator_, other.own_allocator_); + swap(initial_size_, other.initial_size_); + swap(buffer_minalign_, other.buffer_minalign_); + swap(reserved_, other.reserved_); + swap(buf_, other.buf_); + swap(cur_, other.cur_); + swap(scratch_, other.scratch_); + } + + void swap_allocator(vector_downward &other) + { + using std::swap; + swap(allocator_, other.allocator_); + swap(own_allocator_, other.own_allocator_); + } + +private: + // You shouldn't really be copying instances of this class. + FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &)); + FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &)); + + Allocator *allocator_; + bool own_allocator_; + size_t initial_size_; + size_t buffer_minalign_; + size_t reserved_; + uint8_t *buf_; + uint8_t *cur_; // Points at location between empty (below) and used (above). + uint8_t *scratch_; // Points to the end of the scratchpad in use. + + void reallocate(size_t len) + { + auto old_reserved = reserved_; + auto old_size = size(); + auto old_scratch_size = scratch_size(); + reserved_ += (std::max)(len, old_reserved ? old_reserved / 2 : initial_size_); + reserved_ = (reserved_ + buffer_minalign_ - 1) & ~(buffer_minalign_ - 1); + if (buf_) + { + buf_ = + ReallocateDownward(allocator_, buf_, old_reserved, reserved_, old_size, old_scratch_size); + } + else + { + buf_ = Allocate(allocator_, reserved_); + } + cur_ = buf_ + reserved_ - old_size; + scratch_ = buf_ + old_scratch_size; + } +}; + +// Converts a Field ID to a virtual table offset. +inline voffset_t FieldIndexToOffset(voffset_t field_id) +{ + // Should correspond to what EndTable() below builds up. + const int fixed_fields = 2; // Vtable size and Object Size. + return static_cast((field_id + fixed_fields) * sizeof(voffset_t)); +} + +template const T *data(const std::vector &v) +{ + // Eventually the returned pointer gets passed down to memcpy, so + // we need it to be non-null to avoid undefined behavior. + static uint8_t t; + return v.empty() ? reinterpret_cast(&t) : &v.front(); +} +template T *data(std::vector &v) +{ + // Eventually the returned pointer gets passed down to memcpy, so + // we need it to be non-null to avoid undefined behavior. + static uint8_t t; + return v.empty() ? reinterpret_cast(&t) : &v.front(); +} + +/// @endcond + +/// @addtogroup flatbuffers_cpp_api +/// @{ +/// @class FlatBufferBuilder +/// @brief Helper class to hold data needed in creation of a FlatBuffer. +/// To serialize data, you typically call one of the `Create*()` functions in +/// the generated code, which in turn call a sequence of `StartTable`/ +/// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ +/// `CreateVector` functions. Do this is depth-first order to build up a tree to +/// the root. `Finish()` wraps up the buffer ready for transport. +class FlatBufferBuilder +{ +public: + /// @brief Default constructor for FlatBufferBuilder. + /// @param[in] initial_size The initial size of the buffer, in bytes. Defaults + /// to `1024`. + /// @param[in] allocator An `Allocator` to use. If null will use + /// `DefaultAllocator`. + /// @param[in] own_allocator Whether the builder/vector should own the + /// allocator. Defaults to / `false`. + /// @param[in] buffer_minalign Force the buffer to be aligned to the given + /// minimum alignment upon reallocation. Only needed if you intend to store + /// types with custom alignment AND you wish to read the buffer in-place + /// directly after creation. + explicit FlatBufferBuilder(size_t initial_size = 1024, Allocator *allocator = nullptr, + bool own_allocator = false, + size_t buffer_minalign = AlignOf()) + : buf_(initial_size, allocator, own_allocator, buffer_minalign), num_field_loc(0), + max_voffset_(0), nested(false), finished(false), minalign_(1), force_defaults_(false), + dedup_vtables_(true), string_pool(nullptr) + { + EndianCheck(); + } + +/// @brief Move constructor for FlatBufferBuilder. +#if !defined(FLATBUFFERS_CPP98_STL) + FlatBufferBuilder(FlatBufferBuilder &&other) +#else + FlatBufferBuilder(FlatBufferBuilder &other) +#endif // #if !defined(FLATBUFFERS_CPP98_STL) + : buf_(1024, nullptr, false, AlignOf()), num_field_loc(0), max_voffset_(0), + nested(false), finished(false), minalign_(1), force_defaults_(false), dedup_vtables_(true), + string_pool(nullptr) + { + EndianCheck(); + // Default construct and swap idiom. + // Lack of delegating constructors in vs2010 makes it more verbose than needed. + Swap(other); + } + +#if !defined(FLATBUFFERS_CPP98_STL) + // clang-format on + /// @brief Move assignment operator for FlatBufferBuilder. + FlatBufferBuilder &operator=(FlatBufferBuilder &&other) + { + // Move construct a temporary and swap idiom + FlatBufferBuilder temp(std::move(other)); + Swap(temp); + return *this; + } +#endif // defined(FLATBUFFERS_CPP98_STL) + // clang-format on + + void Swap(FlatBufferBuilder &other) + { + using std::swap; + buf_.swap(other.buf_); + swap(num_field_loc, other.num_field_loc); + swap(max_voffset_, other.max_voffset_); + swap(nested, other.nested); + swap(finished, other.finished); + swap(minalign_, other.minalign_); + swap(force_defaults_, other.force_defaults_); + swap(dedup_vtables_, other.dedup_vtables_); + swap(string_pool, other.string_pool); + } + + ~FlatBufferBuilder() + { + if (string_pool) + delete string_pool; + } + + void Reset() + { + Clear(); // clear builder state + buf_.reset(); // deallocate buffer + } + + /// @brief Reset all the state in this FlatBufferBuilder so it can be reused + /// to construct another buffer. + void Clear() + { + ClearOffsets(); + buf_.clear(); + nested = false; + finished = false; + minalign_ = 1; + if (string_pool) + string_pool->clear(); + } + + /// @brief The current size of the serialized buffer, counting from the end. + /// @return Returns an `uoffset_t` with the current size of the buffer. + uoffset_t GetSize() const { return buf_.size(); } + + /// @brief Get the serialized buffer (after you call `Finish()`). + /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the + /// buffer. + uint8_t *GetBufferPointer() const + { + Finished(); + return buf_.data(); + } + + /// @brief Get the serialized buffer (after you call `Finish()`) as a span. + /// @return Returns a constructed flatbuffers::span that is a view over the + /// FlatBuffer data inside the buffer. + flatbuffers::span GetBufferSpan() const + { + Finished(); + return flatbuffers::span(buf_.data(), buf_.size()); + } + + /// @brief Get a pointer to an unfinished buffer. + /// @return Returns a `uint8_t` pointer to the unfinished buffer. + uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } + + /// @brief Get the released pointer to the serialized buffer. + /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! + /// @return A `FlatBuffer` that owns the buffer and its allocator and + /// behaves similar to a `unique_ptr` with a deleter. + FLATBUFFERS_ATTRIBUTE(deprecated("use Release() instead")) + DetachedBuffer ReleaseBufferPointer() + { + Finished(); + return buf_.release(); + } + + /// @brief Get the released DetachedBuffer. + /// @return A `DetachedBuffer` that owns the buffer and its allocator. + DetachedBuffer Release() + { + Finished(); + return buf_.release(); + } + + /// @brief Get the released pointer to the serialized buffer. + /// @param size The size of the memory block containing + /// the serialized `FlatBuffer`. + /// @param offset The offset from the released pointer where the finished + /// `FlatBuffer` starts. + /// @return A raw pointer to the start of the memory block containing + /// the serialized `FlatBuffer`. + /// @remark If the allocator is owned, it gets deleted when the destructor is + /// called.. + uint8_t *ReleaseRaw(size_t &size, size_t &offset) + { + Finished(); + return buf_.release_raw(size, offset); + } + + /// @brief get the minimum alignment this buffer needs to be accessed + /// properly. This is only known once all elements have been written (after + /// you call Finish()). You can use this information if you need to embed + /// a FlatBuffer in some other buffer, such that you can later read it + /// without first having to copy it into its own buffer. + size_t GetBufferMinAlignment() const + { + Finished(); + return minalign_; + } + + /// @cond FLATBUFFERS_INTERNAL + void Finished() const + { + // If you get this assert, you're attempting to get access a buffer + // which hasn't been finished yet. Be sure to call + // FlatBufferBuilder::Finish with your root table. + // If you really need to access an unfinished buffer, call + // GetCurrentBufferPointer instead. + FLATBUFFERS_ASSERT(finished); + } + /// @endcond + + /// @brief In order to save space, fields that are set to their default value + /// don't get serialized into the buffer. + /// @param[in] fd When set to `true`, always serializes default values that + /// are set. Optional fields which are not set explicitly, will still not be + /// serialized. + void ForceDefaults(bool fd) { force_defaults_ = fd; } + + /// @brief By default vtables are deduped in order to save space. + /// @param[in] dedup When set to `true`, dedup vtables. + void DedupVtables(bool dedup) { dedup_vtables_ = dedup; } + + /// @cond FLATBUFFERS_INTERNAL + void Pad(size_t num_bytes) { buf_.fill(num_bytes); } + + void TrackMinAlign(size_t elem_size) + { + if (elem_size > minalign_) + minalign_ = elem_size; + } + + void Align(size_t elem_size) + { + TrackMinAlign(elem_size); + buf_.fill(PaddingBytes(buf_.size(), elem_size)); + } + + void PushFlatBuffer(const uint8_t *bytes, size_t size) + { + PushBytes(bytes, size); + finished = true; + } + + void PushBytes(const uint8_t *bytes, size_t size) { buf_.push(bytes, size); } + + void PopBytes(size_t amount) { buf_.pop(amount); } + + template void AssertScalarT() + { + // The code assumes power of 2 sizes and endian-swap-ability. + static_assert(flatbuffers::is_scalar::value, "T must be a scalar type"); + } + + // Write a single aligned scalar to the buffer + template uoffset_t PushElement(T element) + { + AssertScalarT(); + T litle_endian_element = EndianScalar(element); + Align(sizeof(T)); + buf_.push_small(litle_endian_element); + return GetSize(); + } + + template uoffset_t PushElement(Offset off) + { + // Special case for offsets: see ReferTo below. + return PushElement(ReferTo(off.o)); + } + + // When writing fields, we track where they are, so we can create correct + // vtables later. + void TrackField(voffset_t field, uoffset_t off) + { + FieldLoc fl = {off, field}; + buf_.scratch_push_small(fl); + num_field_loc++; + max_voffset_ = (std::max)(max_voffset_, field); + } + + // Like PushElement, but additionally tracks the field this represents. + template void AddElement(voffset_t field, T e, T def) + { + // We don't serialize values equal to the default. + if (IsTheSameAs(e, def) && !force_defaults_) + return; + auto off = PushElement(e); + TrackField(field, off); + } + + template void AddElement(voffset_t field, T e) + { + auto off = PushElement(e); + TrackField(field, off); + } + + template void AddOffset(voffset_t field, Offset off) + { + if (off.IsNull()) + return; // Don't store. + AddElement(field, ReferTo(off.o), static_cast(0)); + } + + template void AddStruct(voffset_t field, const T *structptr) + { + if (!structptr) + return; // Default, don't store. + Align(AlignOf()); + buf_.push_small(*structptr); + TrackField(field, GetSize()); + } + + void AddStructOffset(voffset_t field, uoffset_t off) { TrackField(field, off); } + + // Offsets initially are relative to the end of the buffer (downwards). + // This function converts them to be relative to the current location + // in the buffer (when stored here), pointing upwards. + uoffset_t ReferTo(uoffset_t off) + { + // Align to ensure GetSize() below is correct. + Align(sizeof(uoffset_t)); + // Offset must refer to something already in buffer. + FLATBUFFERS_ASSERT(off && off <= GetSize()); + return GetSize() - off + static_cast(sizeof(uoffset_t)); + } + + void NotNested() + { + // If you hit this, you're trying to construct a Table/Vector/String + // during the construction of its parent table (between the MyTableBuilder + // and table.Finish(). + // Move the creation of these sub-objects to above the MyTableBuilder to + // not get this assert. + // Ignoring this assert may appear to work in simple cases, but the reason + // it is here is that storing objects in-line may cause vtable offsets + // to not fit anymore. It also leads to vtable duplication. + FLATBUFFERS_ASSERT(!nested); + // If you hit this, fields were added outside the scope of a table. + FLATBUFFERS_ASSERT(!num_field_loc); + } + + // From generated code (or from the parser), we call StartTable/EndTable + // with a sequence of AddElement calls in between. + uoffset_t StartTable() + { + NotNested(); + nested = true; + return GetSize(); + } + + // This finishes one serialized object by generating the vtable if it's a + // table, comparing it against existing vtables, and writing the + // resulting vtable offset. + uoffset_t EndTable(uoffset_t start) + { + // If you get this assert, a corresponding StartTable wasn't called. + FLATBUFFERS_ASSERT(nested); + // Write the vtable offset, which is the start of any Table. + // We fill it's value later. + auto vtableoffsetloc = PushElement(0); + // Write a vtable, which consists entirely of voffset_t elements. + // It starts with the number of offsets, followed by a type id, followed + // by the offsets themselves. In reverse: + // Include space for the last offset and ensure empty tables have a + // minimum size. + max_voffset_ = + (std::max)(static_cast(max_voffset_ + sizeof(voffset_t)), FieldIndexToOffset(0)); + buf_.fill_big(max_voffset_); + auto table_object_size = vtableoffsetloc - start; + // Vtable use 16bit offsets. + FLATBUFFERS_ASSERT(table_object_size < 0x10000); + WriteScalar(buf_.data() + sizeof(voffset_t), + static_cast(table_object_size)); + WriteScalar(buf_.data(), max_voffset_); + // Write the offsets into the table + for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); it < buf_.scratch_end(); + it += sizeof(FieldLoc)) + { + auto field_location = reinterpret_cast(it); + auto pos = static_cast(vtableoffsetloc - field_location->off); + // If this asserts, it means you've set a field twice. + FLATBUFFERS_ASSERT(!ReadScalar(buf_.data() + field_location->id)); + WriteScalar(buf_.data() + field_location->id, pos); + } + ClearOffsets(); + auto vt1 = reinterpret_cast(buf_.data()); + auto vt1_size = ReadScalar(vt1); + auto vt_use = GetSize(); + // See if we already have generated a vtable with this exact same + // layout before. If so, make it point to the old one, remove this one. + if (dedup_vtables_) + { + for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); it += sizeof(uoffset_t)) + { + auto vt_offset_ptr = reinterpret_cast(it); + auto vt2 = reinterpret_cast(buf_.data_at(*vt_offset_ptr)); + auto vt2_size = ReadScalar(vt2); + if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) + continue; + vt_use = *vt_offset_ptr; + buf_.pop(GetSize() - vtableoffsetloc); + break; + } + } + // If this is a new vtable, remember it. + if (vt_use == GetSize()) + { + buf_.scratch_push_small(vt_use); + } + // Fill the vtable offset we created above. + // The offset points from the beginning of the object to where the + // vtable is stored. + // Offsets default direction is downward in memory for future format + // flexibility (storing all vtables at the start of the file). + WriteScalar(buf_.data_at(vtableoffsetloc), + static_cast(vt_use) - static_cast(vtableoffsetloc)); + + nested = false; + return vtableoffsetloc; + } + + FLATBUFFERS_ATTRIBUTE(deprecated("call the version above instead")) + uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) { return EndTable(start); } + + // This checks a required field has been set in a given table that has + // just been constructed. + template void Required(Offset table, voffset_t field); + + uoffset_t StartStruct(size_t alignment) + { + Align(alignment); + return GetSize(); + } + + uoffset_t EndStruct() { return GetSize(); } + + void ClearOffsets() + { + buf_.scratch_pop(num_field_loc * sizeof(FieldLoc)); + num_field_loc = 0; + max_voffset_ = 0; + } + + // Aligns such that when "len" bytes are written, an object can be written + // after it with "alignment" without padding. + void PreAlign(size_t len, size_t alignment) + { + TrackMinAlign(alignment); + buf_.fill(PaddingBytes(GetSize() + len, alignment)); + } + template void PreAlign(size_t len) + { + AssertScalarT(); + PreAlign(len, sizeof(T)); + } + /// @endcond + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const char pointer to the data to be stored as a string. + /// @param[in] len The number of bytes that should be stored from `str`. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const char *str, size_t len) + { + NotNested(); + PreAlign(len + 1); // Always 0-terminated. + buf_.fill(1); + PushBytes(reinterpret_cast(str), len); + PushElement(static_cast(len)); + return Offset(GetSize()); + } + + /// @brief Store a string in the buffer, which is null-terminated. + /// @param[in] str A const char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const char *str) { return CreateString(str, strlen(str)); } + + /// @brief Store a string in the buffer, which is null-terminated. + /// @param[in] str A char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(char *str) { return CreateString(str, strlen(str)); } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const reference to a std::string to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(const std::string &str) + { + return CreateString(str.c_str(), str.length()); + } +#ifdef FLATBUFFERS_HAS_STRING_VIEW + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const string_view to copy in to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateString(flatbuffers::string_view str) + { + return CreateString(str.data(), str.size()); + } +#endif // FLATBUFFERS_HAS_STRING_VIEW + // clang-format on + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const pointer to a `String` struct to add to the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateString(const String *str) + { + return str ? CreateString(str->c_str(), str->size()) : 0; + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// @param[in] str A const reference to a std::string like type with support + /// of T::c_str() and T::length() to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + template Offset CreateString(const T &str) + { + return CreateString(str.c_str(), str.length()); + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const char pointer to the data to be stored as a string. + /// @param[in] len The number of bytes that should be stored from `str`. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const char *str, size_t len) + { + if (!string_pool) + string_pool = new StringOffsetMap(StringOffsetCompare(buf_)); + auto size_before_string = buf_.size(); + // Must first serialize the string, since the set is all offsets into + // buffer. + auto off = CreateString(str, len); + auto it = string_pool->find(off); + // If it exists we reuse existing serialized data! + if (it != string_pool->end()) + { + // We can remove the string we serialized. + buf_.pop(buf_.size() - size_before_string); + return *it; + } + // Record this string for future use. + string_pool->insert(off); + return off; + } + +#ifdef FLATBUFFERS_HAS_STRING_VIEW + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const std::string_view to store in the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateSharedString(const flatbuffers::string_view str) + { + return CreateSharedString(str.data(), str.size()); + } +#else + /// @brief Store a string in the buffer, which null-terminated. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const char pointer to a C-string to add to the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const char *str) + { + return CreateSharedString(str, strlen(str)); + } + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const reference to a std::string to store in the buffer. + /// @return Returns the offset in the buffer where the string starts. + Offset CreateSharedString(const std::string &str) + { + return CreateSharedString(str.c_str(), str.length()); + } +#endif + + /// @brief Store a string in the buffer, which can contain any binary data. + /// If a string with this exact contents has already been serialized before, + /// instead simply returns the offset of the existing string. + /// @param[in] str A const pointer to a `String` struct to add to the buffer. + /// @return Returns the offset in the buffer where the string starts + Offset CreateSharedString(const String *str) + { + return CreateSharedString(str->c_str(), str->size()); + } + + /// @cond FLATBUFFERS_INTERNAL + uoffset_t EndVector(size_t len) + { + FLATBUFFERS_ASSERT(nested); // Hit if no corresponding StartVector. + nested = false; + return PushElement(static_cast(len)); + } + + void StartVector(size_t len, size_t elemsize) + { + NotNested(); + nested = true; + PreAlign(len * elemsize); + PreAlign(len * elemsize, elemsize); // Just in case elemsize > uoffset_t. + } + + // Call this right before StartVector/CreateVector if you want to force the + // alignment to be something different than what the element size would + // normally dictate. + // This is useful when storing a nested_flatbuffer in a vector of bytes, + // or when storing SIMD floats, etc. + void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) + { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); + PreAlign(len * elemsize, alignment); + } + + // Similar to ForceVectorAlignment but for String fields. + void ForceStringAlignment(size_t len, size_t alignment) + { + FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment)); + PreAlign((len + 1) * sizeof(char), alignment); + } + + /// @endcond + + /// @brief Serialize an array into a FlatBuffer `vector`. + /// @tparam T The data type of the array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVector(const T *v, size_t len) + { + // If this assert hits, you're specifying a template argument that is + // causing the wrong overload to be selected, remove it. + AssertScalarT(); + StartVector(len, sizeof(T)); + if (len == 0) + { + return Offset>(EndVector(len)); + } + +#if FLATBUFFERS_LITTLEENDIAN + PushBytes(reinterpret_cast(v), len * sizeof(T)); +#else + if (sizeof(T) == 1) + { + PushBytes(reinterpret_cast(v), len); + } + else + { + for (auto i = len; i > 0;) + { + PushElement(v[--i]); + } + } +#endif + // clang-format on + return Offset>(EndVector(len)); + } + + template Offset>> CreateVector(const Offset *v, size_t len) + { + StartVector(len, sizeof(Offset)); + for (auto i = len; i > 0;) + { + PushElement(v[--i]); + } + return Offset>>(EndVector(len)); + } + + /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. + /// @tparam T The data type of the `std::vector` elements. + /// @param v A const reference to the `std::vector` to serialize into the + /// buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVector(const std::vector &v) + { + return CreateVector(data(v), v.size()); + } + + // vector may be implemented using a bit-set, so we can't access it as + // an array. Instead, read elements manually. + // Background: https://isocpp.org/blog/2012/11/on-vectorbool + Offset> CreateVector(const std::vector &v) + { + StartVector(v.size(), sizeof(uint8_t)); + for (auto i = v.size(); i > 0;) + { + PushElement(static_cast(v[--i])); + } + return Offset>(EndVector(v.size())); + } + +#ifndef FLATBUFFERS_CPP98_STL + /// @brief Serialize values returned by a function into a FlatBuffer `vector`. + /// This is a convenience function that takes care of iteration for you. + /// @tparam T The data type of the `std::vector` elements. + /// @param f A function that takes the current iteration 0..vector_size-1 and + /// returns any type that you can construct a FlatBuffers vector out of. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVector(size_t vector_size, const std::function &f) + { + std::vector elems(vector_size); + for (size_t i = 0; i < vector_size; i++) + elems[i] = f(i); + return CreateVector(elems); + } +#endif + // clang-format on + + /// @brief Serialize values returned by a function into a FlatBuffer `vector`. + /// This is a convenience function that takes care of iteration for you. + /// @tparam T The data type of the `std::vector` elements. + /// @param f A function that takes the current iteration 0..vector_size-1, + /// and the state parameter returning any type that you can construct a + /// FlatBuffers vector out of. + /// @param state State passed to f. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVector(size_t vector_size, F f, S *state) + { + std::vector elems(vector_size); + for (size_t i = 0; i < vector_size; i++) + elems[i] = f(i, state); + return CreateVector(elems); + } + + /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. + /// This is a convenience function for a common case. + /// @param v A const reference to the `std::vector` to serialize into the + /// buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + Offset>> CreateVectorOfStrings(const std::vector &v) + { + std::vector> offsets(v.size()); + for (size_t i = 0; i < v.size(); i++) + offsets[i] = CreateString(v[i]); + return CreateVector(offsets); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVectorOfStructs(const T *v, size_t len) + { + StartVector(len * sizeof(T) / AlignOf(), AlignOf()); + PushBytes(reinterpret_cast(v), sizeof(T) * len); + return Offset>(EndVector(len)); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs(const S *v, size_t len, + T((*const pack_func)(const S &))) + { + FLATBUFFERS_ASSERT(pack_func); + std::vector vv(len); + std::transform(v, v + len, vv.begin(), pack_func); + return CreateVectorOfStructs(data(vv), vv.size()); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs(const S *v, size_t len) + { + extern T Pack(const S &); + return CreateVectorOfNativeStructs(v, len, Pack); + } + +#ifndef FLATBUFFERS_CPP98_STL + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] filler A function that takes the current iteration 0..vector_size-1 + /// and a pointer to the struct that must be filled. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + /// This is mostly useful when flatbuffers are generated with mutation + /// accessors. + template + Offset> CreateVectorOfStructs(size_t vector_size, + const std::function &filler) + { + T *structs = StartVectorOfStructs(vector_size); + for (size_t i = 0; i < vector_size; i++) + { + filler(i, structs); + structs++; + } + return EndVectorOfStructs(vector_size); + } +#endif + // clang-format on + + /// @brief Serialize an array of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the struct array elements. + /// @param[in] f A function that takes the current iteration 0..vector_size-1, + /// a pointer to the struct that must be filled and the state argument. + /// @param[in] state Arbitrary state to pass to f. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + /// This is mostly useful when flatbuffers are generated with mutation + /// accessors. + template + Offset> CreateVectorOfStructs(size_t vector_size, F f, S *state) + { + T *structs = StartVectorOfStructs(vector_size); + for (size_t i = 0; i < vector_size; i++) + { + f(i, structs, state); + structs++; + } + return EndVectorOfStructs(vector_size); + } + + /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfStructs(const std::vector &v) + { + return CreateVectorOfStructs(data(v), v.size()); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @param[in] pack_func Pointer to a function to convert the native struct + /// to the FlatBuffer struct. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs(const std::vector &v, + T((*const pack_func)(const S &))) + { + return CreateVectorOfNativeStructs(data(v), v.size(), pack_func); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector`. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfNativeStructs(const std::vector &v) + { + return CreateVectorOfNativeStructs(data(v), v.size()); + } + + /// @cond FLATBUFFERS_INTERNAL + template struct StructKeyComparator + { + bool operator()(const T &a, const T &b) const { return a.KeyCompareLessThan(&b); } + + FLATBUFFERS_DELETE_FUNC(StructKeyComparator &operator=(const StructKeyComparator &)); + }; + /// @endcond + + /// @brief Serialize a `std::vector` of structs into a FlatBuffer `vector` + /// in sorted order. + /// @tparam T The data type of the `std::vector` struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVectorOfSortedStructs(std::vector *v) + { + return CreateVectorOfSortedStructs(data(*v), v->size()); + } + + /// @brief Serialize a `std::vector` of native structs into a FlatBuffer + /// `vector` in sorted order. + /// @tparam T The data type of the `std::vector` struct elements. + /// @tparam S The data type of the `std::vector` native struct elements. + /// @param[in] v A const reference to the `std::vector` of structs to + /// serialize into the buffer as a `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedNativeStructs(std::vector *v) + { + return CreateVectorOfSortedNativeStructs(data(*v), v->size()); + } + + /// @brief Serialize an array of structs into a FlatBuffer `vector` in sorted + /// order. + /// @tparam T The data type of the struct array elements. + /// @param[in] v A pointer to the array of type `T` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template Offset> CreateVectorOfSortedStructs(T *v, size_t len) + { + std::sort(v, v + len, StructKeyComparator()); + return CreateVectorOfStructs(v, len); + } + + /// @brief Serialize an array of native structs into a FlatBuffer `vector` in + /// sorted order. + /// @tparam T The data type of the struct array elements. + /// @tparam S The data type of the native struct array elements. + /// @param[in] v A pointer to the array of type `S` to serialize into the + /// buffer as a `vector`. + /// @param[in] len The number of elements to serialize. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset> CreateVectorOfSortedNativeStructs(S *v, size_t len) + { + extern T Pack(const S &); + typedef T (*Pack_t)(const S &); + std::vector vv(len); + std::transform(v, v + len, vv.begin(), static_cast(Pack)); + return CreateVectorOfSortedStructs(vv, len); + } + + /// @cond FLATBUFFERS_INTERNAL + template struct TableKeyComparator + { + TableKeyComparator(vector_downward &buf) : buf_(buf) {} + TableKeyComparator(const TableKeyComparator &other) : buf_(other.buf_) {} + bool operator()(const Offset &a, const Offset &b) const + { + auto table_a = reinterpret_cast(buf_.data_at(a.o)); + auto table_b = reinterpret_cast(buf_.data_at(b.o)); + return table_a->KeyCompareLessThan(table_b); + } + vector_downward &buf_; + + private: + FLATBUFFERS_DELETE_FUNC(TableKeyComparator &operator=(const TableKeyComparator &other)); + }; + /// @endcond + + /// @brief Serialize an array of `table` offsets as a `vector` in the buffer + /// in sorted order. + /// @tparam T The data type that the offset refers to. + /// @param[in] v An array of type `Offset` that contains the `table` + /// offsets to store in the buffer in sorted order. + /// @param[in] len The number of elements to store in the `vector`. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfSortedTables(Offset *v, size_t len) + { + std::sort(v, v + len, TableKeyComparator(buf_)); + return CreateVector(v, len); + } + + /// @brief Serialize an array of `table` offsets as a `vector` in the buffer + /// in sorted order. + /// @tparam T The data type that the offset refers to. + /// @param[in] v An array of type `Offset` that contains the `table` + /// offsets to store in the buffer in sorted order. + /// @return Returns a typed `Offset` into the serialized data indicating + /// where the vector is stored. + template + Offset>> CreateVectorOfSortedTables(std::vector> *v) + { + return CreateVectorOfSortedTables(data(*v), v->size()); + } + + /// @brief Specialized version of `CreateVector` for non-copying use cases. + /// Write the data any time later to the returned buffer pointer `buf`. + /// @param[in] len The number of elements to store in the `vector`. + /// @param[in] elemsize The size of each element in the `vector`. + /// @param[out] buf A pointer to a `uint8_t` pointer that can be + /// written to at a later time to serialize the data into a `vector` + /// in the buffer. + uoffset_t CreateUninitializedVector(size_t len, size_t elemsize, uint8_t **buf) + { + NotNested(); + StartVector(len, elemsize); + buf_.make_space(len * elemsize); + auto vec_start = GetSize(); + auto vec_end = EndVector(len); + *buf = buf_.data_at(vec_start); + return vec_end; + } + + /// @brief Specialized version of `CreateVector` for non-copying use cases. + /// Write the data any time later to the returned buffer pointer `buf`. + /// @tparam T The data type of the data that will be stored in the buffer + /// as a `vector`. + /// @param[in] len The number of elements to store in the `vector`. + /// @param[out] buf A pointer to a pointer of type `T` that can be + /// written to at a later time to serialize the data into a `vector` + /// in the buffer. + template Offset> CreateUninitializedVector(size_t len, T **buf) + { + AssertScalarT(); + return CreateUninitializedVector(len, sizeof(T), reinterpret_cast(buf)); + } + + template + Offset> CreateUninitializedVectorOfStructs(size_t len, T **buf) + { + return CreateUninitializedVector(len, sizeof(T), reinterpret_cast(buf)); + } + + // @brief Create a vector of scalar type T given as input a vector of scalar + // type U, useful with e.g. pre "enum class" enums, or any existing scalar + // data of the wrong type. + template Offset> CreateVectorScalarCast(const U *v, size_t len) + { + AssertScalarT(); + AssertScalarT(); + StartVector(len, sizeof(T)); + for (auto i = len; i > 0;) + { + PushElement(static_cast(v[--i])); + } + return Offset>(EndVector(len)); + } + + /// @brief Write a struct by itself, typically to be part of a union. + template Offset CreateStruct(const T &structobj) + { + NotNested(); + Align(AlignOf()); + buf_.push_small(structobj); + return Offset(GetSize()); + } + + /// @brief The length of a FlatBuffer file header. + static const size_t kFileIdentifierLength = 4; + + /// @brief Finish serializing a buffer by writing the root offset. + /// @param[in] file_identifier If a `file_identifier` is given, the buffer + /// will be prefixed with a standard FlatBuffers file header. + template void Finish(Offset root, const char *file_identifier = nullptr) + { + Finish(root.o, file_identifier, false); + } + + /// @brief Finish a buffer with a 32 bit size field pre-fixed (size of the + /// buffer following the size field). These buffers are NOT compatible + /// with standard buffers created by Finish, i.e. you can't call GetRoot + /// on them, you have to use GetSizePrefixedRoot instead. + /// All >32 bit quantities in this buffer will be aligned when the whole + /// size pre-fixed buffer is aligned. + /// These kinds of buffers are useful for creating a stream of FlatBuffers. + template + void FinishSizePrefixed(Offset root, const char *file_identifier = nullptr) + { + Finish(root.o, file_identifier, true); + } + + void SwapBufAllocator(FlatBufferBuilder &other) { buf_.swap_allocator(other.buf_); } + +protected: + // You shouldn't really be copying instances of this class. + FlatBufferBuilder(const FlatBufferBuilder &); + FlatBufferBuilder &operator=(const FlatBufferBuilder &); + + void Finish(uoffset_t root, const char *file_identifier, bool size_prefix) + { + NotNested(); + buf_.clear_scratch(); + // This will cause the whole buffer to be aligned. + PreAlign((size_prefix ? sizeof(uoffset_t) : 0) + sizeof(uoffset_t) + + (file_identifier ? kFileIdentifierLength : 0), + minalign_); + if (file_identifier) + { + FLATBUFFERS_ASSERT(strlen(file_identifier) == kFileIdentifierLength); + PushBytes(reinterpret_cast(file_identifier), kFileIdentifierLength); + } + PushElement(ReferTo(root)); // Location of root. + if (size_prefix) + { + PushElement(GetSize()); + } + finished = true; + } + + struct FieldLoc + { + uoffset_t off; + voffset_t id; + }; + + vector_downward buf_; + + // Accumulating offsets of table members while it is being built. + // We store these in the scratch pad of buf_, after the vtable offsets. + uoffset_t num_field_loc; + // Track how much of the vtable is in use, so we can output the most compact + // possible vtable. + voffset_t max_voffset_; + + // Ensure objects are not nested. + bool nested; + + // Ensure the buffer is finished before it is being accessed. + bool finished; + + size_t minalign_; + + bool force_defaults_; // Serialize values equal to their defaults anyway. + + bool dedup_vtables_; + + struct StringOffsetCompare + { + StringOffsetCompare(const vector_downward &buf) : buf_(&buf) {} + bool operator()(const Offset &a, const Offset &b) const + { + auto stra = reinterpret_cast(buf_->data_at(a.o)); + auto strb = reinterpret_cast(buf_->data_at(b.o)); + return StringLessThan(stra->data(), stra->size(), strb->data(), strb->size()); + } + const vector_downward *buf_; + }; + + // For use with CreateSharedString. Instantiated on first use only. + typedef std::set, StringOffsetCompare> StringOffsetMap; + StringOffsetMap *string_pool; + +private: + // Allocates space for a vector of structures. + // Must be completed with EndVectorOfStructs(). + template T *StartVectorOfStructs(size_t vector_size) + { + StartVector(vector_size * sizeof(T) / AlignOf(), AlignOf()); + return reinterpret_cast(buf_.make_space(vector_size * sizeof(T))); + } + + // End the vector of structues in the flatbuffers. + // Vector should have previously be started with StartVectorOfStructs(). + template Offset> EndVectorOfStructs(size_t vector_size) + { + return Offset>(EndVector(vector_size)); + } +}; +/// @} + +/// @cond FLATBUFFERS_INTERNAL +// Helpers to get a typed pointer to the root object contained in the buffer. +template T *GetMutableRoot(void *buf) +{ + EndianCheck(); + return reinterpret_cast(reinterpret_cast(buf) + + EndianScalar(*reinterpret_cast(buf))); +} + +template const T *GetRoot(const void *buf) +{ + return GetMutableRoot(const_cast(buf)); +} + +template const T *GetSizePrefixedRoot(const void *buf) +{ + return GetRoot(reinterpret_cast(buf) + sizeof(uoffset_t)); +} + +/// Helpers to get a typed pointer to objects that are currently being built. +/// @warning Creating new objects will lead to reallocations and invalidates +/// the pointer! +template T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) +{ + return reinterpret_cast(fbb.GetCurrentBufferPointer() + fbb.GetSize() - offset.o); +} + +template const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) +{ + return GetMutableTemporaryPointer(fbb, offset); +} + +/// @brief Get a pointer to the the file_identifier section of the buffer. +/// @return Returns a const char pointer to the start of the file_identifier +/// characters in the buffer. The returned char * has length +/// 'flatbuffers::FlatBufferBuilder::kFileIdentifierLength'. +/// This function is UNDEFINED for FlatBuffers whose schema does not include +/// a file_identifier (likely points at padding or the start of a the root +/// vtable). +inline const char *GetBufferIdentifier(const void *buf, bool size_prefixed = false) +{ + return reinterpret_cast(buf) + + ((size_prefixed) ? 2 * sizeof(uoffset_t) : sizeof(uoffset_t)); +} + +// Helper to see if the identifier in a buffer has the expected value. +inline bool BufferHasIdentifier(const void *buf, const char *identifier, bool size_prefixed = false) +{ + return strncmp(GetBufferIdentifier(buf, size_prefixed), identifier, + FlatBufferBuilder::kFileIdentifierLength) == 0; +} + +// Helper class to verify the integrity of a FlatBuffer +class Verifier FLATBUFFERS_FINAL_CLASS +{ +public: + Verifier(const uint8_t *buf, size_t buf_len, uoffset_t _max_depth = 64, + uoffset_t _max_tables = 1000000, bool _check_alignment = true) + : buf_(buf), size_(buf_len), depth_(0), max_depth_(_max_depth), num_tables_(0), + max_tables_(_max_tables), upper_bound_(0), check_alignment_(_check_alignment) + { + FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE); + } + + // Central location where any verification failures register. + bool Check(bool ok) const + { +#ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE + FLATBUFFERS_ASSERT(ok); +#endif +#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + if (!ok) + upper_bound_ = 0; +#endif + // clang-format on + return ok; + } + + // Verify any range within the buffer. + bool Verify(size_t elem, size_t elem_len) const + { +#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + auto upper_bound = elem + elem_len; + if (upper_bound_ < upper_bound) + upper_bound_ = upper_bound; +#endif + // clang-format on + return Check(elem_len < size_ && elem <= size_ - elem_len); + } + + template bool VerifyAlignment(size_t elem) const + { + return Check((elem & (sizeof(T) - 1)) == 0 || !check_alignment_); + } + + // Verify a range indicated by sizeof(T). + template bool Verify(size_t elem) const + { + return VerifyAlignment(elem) && Verify(elem, sizeof(T)); + } + + bool VerifyFromPointer(const uint8_t *p, size_t len) + { + auto o = static_cast(p - buf_); + return Verify(o, len); + } + + // Verify relative to a known-good base pointer. + bool Verify(const uint8_t *base, voffset_t elem_off, size_t elem_len) const + { + return Verify(static_cast(base - buf_) + elem_off, elem_len); + } + + template bool Verify(const uint8_t *base, voffset_t elem_off) const + { + return Verify(static_cast(base - buf_) + elem_off, sizeof(T)); + } + + // Verify a pointer (may be NULL) of a table type. + template bool VerifyTable(const T *table) { return !table || table->Verify(*this); } + + // Verify a pointer (may be NULL) of any vector type. + template bool VerifyVector(const Vector *vec) const + { + return !vec || VerifyVectorOrString(reinterpret_cast(vec), sizeof(T)); + } + + // Verify a pointer (may be NULL) of a vector to struct. + template bool VerifyVector(const Vector *vec) const + { + return VerifyVector(reinterpret_cast *>(vec)); + } + + // Verify a pointer (may be NULL) to string. + bool VerifyString(const String *str) const + { + size_t end; + return !str || (VerifyVectorOrString(reinterpret_cast(str), 1, &end) && + Verify(end, 1) && // Must have terminator + Check(buf_[end] == '\0')); // Terminating byte must be 0. + } + + // Common code between vectors and strings. + bool VerifyVectorOrString(const uint8_t *vec, size_t elem_size, size_t *end = nullptr) const + { + auto veco = static_cast(vec - buf_); + // Check we can read the size field. + if (!Verify(veco)) + return false; + // Check the whole array. If this is a string, the byte past the array + // must be 0. + auto size = ReadScalar(vec); + auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size; + if (!Check(size < max_elems)) + return false; // Protect against byte_size overflowing. + auto byte_size = sizeof(size) + elem_size * size; + if (end) + *end = veco + byte_size; + return Verify(veco, byte_size); + } + + // Special case for string contents, after the above has been called. + bool VerifyVectorOfStrings(const Vector> *vec) const + { + if (vec) + { + for (uoffset_t i = 0; i < vec->size(); i++) + { + if (!VerifyString(vec->Get(i))) + return false; + } + } + return true; + } + + // Special case for table contents, after the above has been called. + template bool VerifyVectorOfTables(const Vector> *vec) + { + if (vec) + { + for (uoffset_t i = 0; i < vec->size(); i++) + { + if (!vec->Get(i)->Verify(*this)) + return false; + } + } + return true; + } + + __supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(const uint8_t *table) + { + // Check the vtable offset. + auto tableo = static_cast(table - buf_); + if (!Verify(tableo)) + return false; + // This offset may be signed, but doing the subtraction unsigned always + // gives the result we want. + auto vtableo = tableo - static_cast(ReadScalar(table)); + // Check the vtable size field, then check vtable fits in its entirety. + return VerifyComplexity() && Verify(vtableo) && + VerifyAlignment(ReadScalar(buf_ + vtableo)) && + Verify(vtableo, ReadScalar(buf_ + vtableo)); + } + + template bool VerifyBufferFromStart(const char *identifier, size_t start) + { + if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) && + BufferHasIdentifier(buf_ + start, identifier)))) + { + return false; + } + + // Call T::Verify, which must be in the generated code for this type. + auto o = VerifyOffset(start); + return o && reinterpret_cast(buf_ + start + o)->Verify(*this) +#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + && GetComputedSize() +#endif + ; + // clang-format on + } + + // Verify this whole buffer, starting with root type T. + template bool VerifyBuffer() { return VerifyBuffer(nullptr); } + + template bool VerifyBuffer(const char *identifier) + { + return VerifyBufferFromStart(identifier, 0); + } + + template bool VerifySizePrefixedBuffer(const char *identifier) + { + return Verify(0U) && ReadScalar(buf_) == size_ - sizeof(uoffset_t) && + VerifyBufferFromStart(identifier, sizeof(uoffset_t)); + } + + uoffset_t VerifyOffset(size_t start) const + { + if (!Verify(start)) + return 0; + auto o = ReadScalar(buf_ + start); + // May not point to itself. + if (!Check(o != 0)) + return 0; + // Can't wrap around / buffers are max 2GB. + if (!Check(static_cast(o) >= 0)) + return 0; + // Must be inside the buffer to create a pointer from it (pointer outside + // buffer is UB). + if (!Verify(start + o, 1)) + return 0; + return o; + } + + uoffset_t VerifyOffset(const uint8_t *base, voffset_t start) const + { + return VerifyOffset(static_cast(base - buf_) + start); + } + + // Called at the start of a table to increase counters measuring data + // structure depth and amount, and possibly bails out with false if + // limits set by the constructor have been hit. Needs to be balanced + // with EndTable(). + bool VerifyComplexity() + { + depth_++; + num_tables_++; + return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_); + } + + // Called at the end of a table to pop the depth count. + bool EndTable() + { + depth_--; + return true; + } + + // Returns the message size in bytes + size_t GetComputedSize() const + { +#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + uintptr_t size = upper_bound_; + // Align the size to uoffset_t + size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); + return (size > size_) ? 0 : size; +#else + // Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work. + (void)upper_bound_; + FLATBUFFERS_ASSERT(false); + return 0; +#endif + // clang-format on + } + +private: + const uint8_t *buf_; + size_t size_; + uoffset_t depth_; + uoffset_t max_depth_; + uoffset_t num_tables_; + uoffset_t max_tables_; + mutable size_t upper_bound_; + bool check_alignment_; +}; + +// Convenient way to bundle a buffer and its length, to pass it around +// typed by its root. +// A BufferRef does not own its buffer. +struct BufferRefBase +{ +}; // for std::is_base_of +template struct BufferRef : BufferRefBase +{ + BufferRef() : buf(nullptr), len(0), must_free(false) {} + BufferRef(uint8_t *_buf, uoffset_t _len) : buf(_buf), len(_len), must_free(false) {} + + ~BufferRef() + { + if (must_free) + free(buf); + } + + const T *GetRoot() const { return flatbuffers::GetRoot(buf); } + + bool Verify() + { + Verifier verifier(buf, len); + return verifier.VerifyBuffer(nullptr); + } + + uint8_t *buf; + uoffset_t len; + bool must_free; +}; + +// "structs" are flat structures that do not have an offset table, thus +// always have all members present and do not support forwards/backwards +// compatible extensions. + +class Struct FLATBUFFERS_FINAL_CLASS +{ +public: + template T GetField(uoffset_t o) const { return ReadScalar(&data_[o]); } + + template T GetStruct(uoffset_t o) const { return reinterpret_cast(&data_[o]); } + + const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; } + uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; } + +private: + // private constructor & copy constructor: you obtain instances of this + // class by pointing to existing data only + Struct(); + Struct(const Struct &); + Struct &operator=(const Struct &); + + uint8_t data_[1]; +}; + +// "tables" use an offset table (possibly shared) that allows fields to be +// omitted and added at will, but uses an extra indirection to read. +class Table +{ +public: + const uint8_t *GetVTable() const { return data_ - ReadScalar(data_); } + + // This gets the field offset for any of the functions below it, or 0 + // if the field was not present. + voffset_t GetOptionalFieldOffset(voffset_t field) const + { + // The vtable offset is always at the start. + auto vtable = GetVTable(); + // The first element is the size of the vtable (fields + type id + itself). + auto vtsize = ReadScalar(vtable); + // If the field we're accessing is outside the vtable, we're reading older + // data, so it's the same as if the offset was 0 (not present). + return field < vtsize ? ReadScalar(vtable + field) : 0; + } + + template T GetField(voffset_t field, T defaultval) const + { + auto field_offset = GetOptionalFieldOffset(field); + return field_offset ? ReadScalar(data_ + field_offset) : defaultval; + } + + template P GetPointer(voffset_t field) + { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? reinterpret_cast

(p + ReadScalar(p)) : nullptr; + } + template P GetPointer(voffset_t field) const + { + return const_cast(this)->GetPointer

(field); + } + + template P GetStruct(voffset_t field) const + { + auto field_offset = GetOptionalFieldOffset(field); + auto p = const_cast(data_ + field_offset); + return field_offset ? reinterpret_cast

(p) : nullptr; + } + + template + flatbuffers::Optional GetOptional(voffset_t field) const + { + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(static_cast(ReadScalar(p))) : Optional(); + } + + template bool SetField(voffset_t field, T val, T def) + { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) + return IsTheSameAs(val, def); + WriteScalar(data_ + field_offset, val); + return true; + } + template bool SetField(voffset_t field, T val) + { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) + return false; + WriteScalar(data_ + field_offset, val); + return true; + } + + bool SetPointer(voffset_t field, const uint8_t *val) + { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) + return false; + WriteScalar(data_ + field_offset, static_cast(val - (data_ + field_offset))); + return true; + } + + uint8_t *GetAddressOf(voffset_t field) + { + auto field_offset = GetOptionalFieldOffset(field); + return field_offset ? data_ + field_offset : nullptr; + } + const uint8_t *GetAddressOf(voffset_t field) const + { + return const_cast

(this)->GetAddressOf(field); + } + + bool CheckField(voffset_t field) const { return GetOptionalFieldOffset(field) != 0; } + + // Verify the vtable of this table. + // Call this once per table, followed by VerifyField once per field. + bool VerifyTableStart(Verifier &verifier) const { return verifier.VerifyTableStart(data_); } + + // Verify a particular field. + template bool VerifyField(const Verifier &verifier, voffset_t field) const + { + // Calling GetOptionalFieldOffset should be safe now thanks to + // VerifyTable(). + auto field_offset = GetOptionalFieldOffset(field); + // Check the actual field. + return !field_offset || verifier.Verify(data_, field_offset); + } + + // VerifyField for required fields. + template bool VerifyFieldRequired(const Verifier &verifier, voffset_t field) const + { + auto field_offset = GetOptionalFieldOffset(field); + return verifier.Check(field_offset != 0) && verifier.Verify(data_, field_offset); + } + + // Versions for offsets. + bool VerifyOffset(const Verifier &verifier, voffset_t field) const + { + auto field_offset = GetOptionalFieldOffset(field); + return !field_offset || verifier.VerifyOffset(data_, field_offset); + } + + bool VerifyOffsetRequired(const Verifier &verifier, voffset_t field) const + { + auto field_offset = GetOptionalFieldOffset(field); + return verifier.Check(field_offset != 0) && verifier.VerifyOffset(data_, field_offset); + } + +private: + // private constructor & copy constructor: you obtain instances of this + // class by pointing to existing data only + Table(); + Table(const Table &other); + Table &operator=(const Table &); + + uint8_t data_[1]; +}; + +// This specialization allows avoiding warnings like: +// MSVC C4800: type: forcing value to bool 'true' or 'false'. +template <> +inline flatbuffers::Optional Table::GetOptional(voffset_t field) const +{ + auto field_offset = GetOptionalFieldOffset(field); + auto p = data_ + field_offset; + return field_offset ? Optional(ReadScalar(p) != 0) : Optional(); +} + +template void FlatBufferBuilder::Required(Offset table, voffset_t field) +{ + auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); + bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; + // If this fails, the caller will show what field needs to be set. + FLATBUFFERS_ASSERT(ok); + (void)ok; +} + +/// @brief This can compute the start of a FlatBuffer from a root pointer, i.e. +/// it is the opposite transformation of GetRoot(). +/// This may be useful if you want to pass on a root and have the recipient +/// delete the buffer afterwards. +inline const uint8_t *GetBufferStartFromRootPointer(const void *root) +{ + auto table = reinterpret_cast(root); + auto vtable = table->GetVTable(); + // Either the vtable is before the root or after the root. + auto start = (std::min)(vtable, reinterpret_cast(root)); + // Align to at least sizeof(uoffset_t). + start = reinterpret_cast(reinterpret_cast(start) & + ~(sizeof(uoffset_t) - 1)); + // Additionally, there may be a file_identifier in the buffer, and the root + // offset. The buffer may have been aligned to any size between + // sizeof(uoffset_t) and FLATBUFFERS_MAX_ALIGNMENT (see "force_align"). + // Sadly, the exact alignment is only known when constructing the buffer, + // since it depends on the presence of values with said alignment properties. + // So instead, we simply look at the next uoffset_t values (root, + // file_identifier, and alignment padding) to see which points to the root. + // None of the other values can "impersonate" the root since they will either + // be 0 or four ASCII characters. + static_assert(FlatBufferBuilder::kFileIdentifierLength == sizeof(uoffset_t), + "file_identifier is assumed to be the same size as uoffset_t"); + for (auto possible_roots = FLATBUFFERS_MAX_ALIGNMENT / sizeof(uoffset_t) + 1; possible_roots; + possible_roots--) + { + start -= sizeof(uoffset_t); + if (ReadScalar(start) + start == reinterpret_cast(root)) + return start; + } + // We didn't find the root, either the "root" passed isn't really a root, + // or the buffer is corrupt. + // Assert, because calling this function with bad data may cause reads + // outside of buffer boundaries. + FLATBUFFERS_ASSERT(false); + return nullptr; +} + +/// @brief This return the prefixed size of a FlatBuffer. +inline uoffset_t GetPrefixedSize(const uint8_t *buf) { return ReadScalar(buf); } + +// Base class for native objects (FlatBuffer data de-serialized into native +// C++ data structures). +// Contains no functionality, purely documentative. +struct NativeTable +{ +}; + +/// @brief Function types to be used with resolving hashes into objects and +/// back again. The resolver gets a pointer to a field inside an object API +/// object that is of the type specified in the schema using the attribute +/// `cpp_type` (it is thus important whatever you write to this address +/// matches that type). The value of this field is initially null, so you +/// may choose to implement a delayed binding lookup using this function +/// if you wish. The resolver does the opposite lookup, for when the object +/// is being serialized again. +typedef uint64_t hash_value_t; +// clang-format off +#ifdef FLATBUFFERS_CPP98_STL + typedef void (*resolver_function_t)(void **pointer_adr, hash_value_t hash); + typedef hash_value_t (*rehasher_function_t)(void *pointer); +#else + typedef std::function + resolver_function_t; + typedef std::function rehasher_function_t; +#endif +// clang-format on + +// Helper function to test if a field is present, using any of the field +// enums in the generated code. +// `table` must be a generated table type. Since this is a template parameter, +// this is not typechecked to be a subclass of Table, so beware! +// Note: this function will return false for fields equal to the default +// value, since they're not stored in the buffer (unless force_defaults was +// used). +template bool IsFieldPresent(const T *table, typename T::FlatBuffersVTableOffset field) +{ + // Cast, since Table is a private baseclass of any table types. + return reinterpret_cast(table)->CheckField(static_cast(field)); +} + +// Utility function for reverse lookups on the EnumNames*() functions +// (in the generated C++ code) +// names must be NULL terminated. +inline int LookupEnum(const char **names, const char *name) +{ + for (const char **p = names; *p; p++) + if (!strcmp(*p, name)) + return static_cast(p - names); + return -1; +} + +// These macros allow us to layout a struct with a guarantee that they'll end +// up looking the same on different compilers and platforms. +// It does this by disallowing the compiler to do any padding, and then +// does padding itself by inserting extra padding fields that make every +// element aligned to its own size. +// Additionally, it manually sets the alignment of the struct as a whole, +// which is typically its largest element, or a custom size set in the schema +// by the force_align attribute. +// These are used in the generated code only. + +// clang-format off +#if defined(_MSC_VER) + #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ + __pragma(pack(1)) \ + struct __declspec(align(alignment)) + #define FLATBUFFERS_STRUCT_END(name, size) \ + __pragma(pack()) \ + static_assert(sizeof(name) == size, "compiler breaks packing rules") +#elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__) + #define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \ + _Pragma("pack(1)") \ + struct __attribute__((aligned(alignment))) + #define FLATBUFFERS_STRUCT_END(name, size) \ + _Pragma("pack()") \ + static_assert(sizeof(name) == size, "compiler breaks packing rules") +#else + #error Unknown compiler, please define structure alignment macros +#endif +// clang-format on + +// Minimal reflection via code generation. +// Besides full-fat reflection (see reflection.h) and parsing/printing by +// loading schemas (see idl.h), we can also have code generation for mimimal +// reflection data which allows pretty-printing and other uses without needing +// a schema or a parser. +// Generate code with --reflect-types (types only) or --reflect-names (names +// also) to enable. +// See minireflect.h for utilities using this functionality. + +// These types are organized slightly differently as the ones in idl.h. +enum SequenceType +{ + ST_TABLE, + ST_STRUCT, + ST_UNION, + ST_ENUM +}; + +// Scalars have the same order as in idl.h +// clang-format off +#define FLATBUFFERS_GEN_ELEMENTARY_TYPES(ET) \ + ET(ET_UTYPE) \ + ET(ET_BOOL) \ + ET(ET_CHAR) \ + ET(ET_UCHAR) \ + ET(ET_SHORT) \ + ET(ET_USHORT) \ + ET(ET_INT) \ + ET(ET_UINT) \ + ET(ET_LONG) \ + ET(ET_ULONG) \ + ET(ET_FLOAT) \ + ET(ET_DOUBLE) \ + ET(ET_STRING) \ + ET(ET_SEQUENCE) // See SequenceType. + +enum ElementaryType { + #define FLATBUFFERS_ET(E) E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET +}; + +inline const char * const *ElementaryTypeNames() { + static const char * const names[] = { + #define FLATBUFFERS_ET(E) #E, + FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET) + #undef FLATBUFFERS_ET + }; + return names; +} +// clang-format on + +// Basic type info cost just 16bits per field! +// We're explicitly defining the signedness since the signedness of integer +// bitfields is otherwise implementation-defined and causes warnings on older +// GCC compilers. +struct TypeCode +{ + // ElementaryType + unsigned short base_type : 4; + // Either vector (in table) or array (in struct) + unsigned short is_repeating : 1; + // Index into type_refs below, or -1 for none. + signed short sequence_ref : 11; +}; + +static_assert(sizeof(TypeCode) == 2, "TypeCode"); + +struct TypeTable; + +// Signature of the static method present in each type. +typedef const TypeTable *(*TypeFunction)(); + +struct TypeTable +{ + SequenceType st; + size_t num_elems; // of type_codes, values, names (but not type_refs). + const TypeCode *type_codes; // num_elems count + const TypeFunction *type_refs; // less than num_elems entries (see TypeCode). + const int16_t *array_sizes; // less than num_elems entries (see TypeCode). + const int64_t *values; // Only set for non-consecutive enum/union or structs. + const char *const *names; // Only set if compiled with --reflect-names. +}; + +// String which identifies the current version of FlatBuffers. +// flatbuffer_version_string is used by Google developers to identify which +// applications uploaded to Google Play are using this library. This allows +// the development team at Google to determine the popularity of the library. +// How it works: Applications that are uploaded to the Google Play Store are +// scanned for this version string. We track which applications are using it +// to measure popularity. You are free to remove it (of course) but we would +// appreciate if you left it in. + +// Weak linkage is culled by VS & doesn't work on cygwin. +// clang-format off +#if !defined(_WIN32) && !defined(__CYGWIN__) + +extern volatile __attribute__((weak)) const char *flatbuffer_version_string; +volatile __attribute__((weak)) const char *flatbuffer_version_string = + "FlatBuffers " + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "." + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "." + FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION); + +#endif // !defined(_WIN32) && !defined(__CYGWIN__) + +#define FLATBUFFERS_DEFINE_BITMASK_OPERATORS(E, T)\ + inline E operator | (E lhs, E rhs){\ + return E(T(lhs) | T(rhs));\ + }\ + inline E operator & (E lhs, E rhs){\ + return E(T(lhs) & T(rhs));\ + }\ + inline E operator ^ (E lhs, E rhs){\ + return E(T(lhs) ^ T(rhs));\ + }\ + inline E operator ~ (E lhs){\ + return E(~T(lhs));\ + }\ + inline E operator |= (E &lhs, E rhs){\ + lhs = lhs | rhs;\ + return lhs;\ + }\ + inline E operator &= (E &lhs, E rhs){\ + lhs = lhs & rhs;\ + return lhs;\ + }\ + inline E operator ^= (E &lhs, E rhs){\ + lhs = lhs ^ rhs;\ + return lhs;\ + }\ + inline bool operator !(E rhs) \ + {\ + return !bool(T(rhs)); \ + } +/// @endcond +} // namespace flatbuffers + +// clang-format on + +#endif // FLATBUFFERS_H_ diff --git a/onert-micro/externals/flatbuffers/flatc.h b/onert-micro/externals/flatbuffers/flatc.h new file mode 100644 index 0000000..594bf79 --- /dev/null +++ b/onert-micro/externals/flatbuffers/flatc.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_FLATC_H_ +#define FLATBUFFERS_FLATC_H_ + +#include +#include +#include + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers +{ + +extern void LogCompilerWarn(const std::string &warn); +extern void LogCompilerError(const std::string &err); + +class FlatCompiler +{ +public: + // Output generator for the various programming languages and formats we + // support. + struct Generator + { + typedef bool (*GenerateFn)(const flatbuffers::Parser &parser, const std::string &path, + const std::string &file_name); + typedef std::string (*MakeRuleFn)(const flatbuffers::Parser &parser, const std::string &path, + const std::string &file_name); + + GenerateFn generate; + const char *generator_opt_short; + const char *generator_opt_long; + const char *lang_name; + bool schema_only; + GenerateFn generateGRPC; + flatbuffers::IDLOptions::Language lang; + const char *generator_help; + MakeRuleFn make_rule; + }; + + typedef void (*WarnFn)(const FlatCompiler *flatc, const std::string &warn, bool show_exe_name); + + typedef void (*ErrorFn)(const FlatCompiler *flatc, const std::string &err, bool usage, + bool show_exe_name); + + // Parameters required to initialize the FlatCompiler. + struct InitParams + { + InitParams() : generators(nullptr), num_generators(0), warn_fn(nullptr), error_fn(nullptr) {} + + const Generator *generators; + size_t num_generators; + WarnFn warn_fn; + ErrorFn error_fn; + }; + + explicit FlatCompiler(const InitParams ¶ms) : params_(params) {} + + int Compile(int argc, const char **argv); + + std::string GetUsageString(const char *program_name) const; + +private: + void ParseFile(flatbuffers::Parser &parser, const std::string &filename, + const std::string &contents, std::vector &include_directories) const; + + void LoadBinarySchema(Parser &parser, const std::string &filename, const std::string &contents); + + void Warn(const std::string &warn, bool show_exe_name = true) const; + + void Error(const std::string &err, bool usage = true, bool show_exe_name = true) const; + + InitParams params_; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_FLATC_H_ diff --git a/onert-micro/externals/flatbuffers/flexbuffers.h b/onert-micro/externals/flatbuffers/flexbuffers.h new file mode 100644 index 0000000..f6fcbf3 --- /dev/null +++ b/onert-micro/externals/flatbuffers/flexbuffers.h @@ -0,0 +1,1852 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_FLEXBUFFERS_H_ +#define FLATBUFFERS_FLEXBUFFERS_H_ + +#include +// Used to select STL variant. +#include "flatbuffers/base.h" +// We use the basic binary writing functions from the regular FlatBuffers. +#include "flatbuffers/util.h" + +#ifdef _MSC_VER +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) // C4127: conditional expression is constant +#endif + +namespace flexbuffers +{ + +class Reference; +class Map; + +// These are used in the lower 2 bits of a type field to determine the size of +// the elements (and or size field) of the item pointed to (e.g. vector). +enum BitWidth +{ + BIT_WIDTH_8 = 0, + BIT_WIDTH_16 = 1, + BIT_WIDTH_32 = 2, + BIT_WIDTH_64 = 3, +}; + +// These are used as the upper 6 bits of a type field to indicate the actual +// type. +enum Type +{ + FBT_NULL = 0, + FBT_INT = 1, + FBT_UINT = 2, + FBT_FLOAT = 3, + // Types above stored inline, types below store an offset. + FBT_KEY = 4, + FBT_STRING = 5, + FBT_INDIRECT_INT = 6, + FBT_INDIRECT_UINT = 7, + FBT_INDIRECT_FLOAT = 8, + FBT_MAP = 9, + FBT_VECTOR = 10, // Untyped. + FBT_VECTOR_INT = 11, // Typed any size (stores no type table). + FBT_VECTOR_UINT = 12, + FBT_VECTOR_FLOAT = 13, + FBT_VECTOR_KEY = 14, + // DEPRECATED, use FBT_VECTOR or FBT_VECTOR_KEY instead. + // Read test.cpp/FlexBuffersDeprecatedTest() for details on why. + FBT_VECTOR_STRING_DEPRECATED = 15, + FBT_VECTOR_INT2 = 16, // Typed tuple (no type table, no size field). + FBT_VECTOR_UINT2 = 17, + FBT_VECTOR_FLOAT2 = 18, + FBT_VECTOR_INT3 = 19, // Typed triple (no type table, no size field). + FBT_VECTOR_UINT3 = 20, + FBT_VECTOR_FLOAT3 = 21, + FBT_VECTOR_INT4 = 22, // Typed quad (no type table, no size field). + FBT_VECTOR_UINT4 = 23, + FBT_VECTOR_FLOAT4 = 24, + FBT_BLOB = 25, + FBT_BOOL = 26, + FBT_VECTOR_BOOL = 36, // To Allow the same type of conversion of type to vector type +}; + +inline bool IsInline(Type t) { return t <= FBT_FLOAT || t == FBT_BOOL; } + +inline bool IsTypedVectorElementType(Type t) +{ + return (t >= FBT_INT && t <= FBT_STRING) || t == FBT_BOOL; +} + +inline bool IsTypedVector(Type t) +{ + return (t >= FBT_VECTOR_INT && t <= FBT_VECTOR_STRING_DEPRECATED) || t == FBT_VECTOR_BOOL; +} + +inline bool IsFixedTypedVector(Type t) { return t >= FBT_VECTOR_INT2 && t <= FBT_VECTOR_FLOAT4; } + +inline Type ToTypedVector(Type t, size_t fixed_len = 0) +{ + FLATBUFFERS_ASSERT(IsTypedVectorElementType(t)); + switch (fixed_len) + { + case 0: + return static_cast(t - FBT_INT + FBT_VECTOR_INT); + case 2: + return static_cast(t - FBT_INT + FBT_VECTOR_INT2); + case 3: + return static_cast(t - FBT_INT + FBT_VECTOR_INT3); + case 4: + return static_cast(t - FBT_INT + FBT_VECTOR_INT4); + default: + FLATBUFFERS_ASSERT(0); + return FBT_NULL; + } +} + +inline Type ToTypedVectorElementType(Type t) +{ + FLATBUFFERS_ASSERT(IsTypedVector(t)); + return static_cast(t - FBT_VECTOR_INT + FBT_INT); +} + +inline Type ToFixedTypedVectorElementType(Type t, uint8_t *len) +{ + FLATBUFFERS_ASSERT(IsFixedTypedVector(t)); + auto fixed_type = t - FBT_VECTOR_INT2; + *len = static_cast(fixed_type / 3 + 2); // 3 types each, starting from length 2. + return static_cast(fixed_type % 3 + FBT_INT); +} + +// TODO: implement proper support for 8/16bit floats, or decide not to +// support them. +typedef int16_t half; +typedef int8_t quarter; + +// TODO: can we do this without conditionals using intrinsics or inline asm +// on some platforms? Given branch prediction the method below should be +// decently quick, but it is the most frequently executed function. +// We could do an (unaligned) 64-bit read if we ifdef out the platforms for +// which that doesn't work (or where we'd read into un-owned memory). +template +R ReadSizedScalar(const uint8_t *data, uint8_t byte_width) +{ + return byte_width < 4 ? (byte_width < 2 ? static_cast(flatbuffers::ReadScalar(data)) + : static_cast(flatbuffers::ReadScalar(data))) + : (byte_width < 8 ? static_cast(flatbuffers::ReadScalar(data)) + : static_cast(flatbuffers::ReadScalar(data))); +} + +inline int64_t ReadInt64(const uint8_t *data, uint8_t byte_width) +{ + return ReadSizedScalar(data, byte_width); +} + +inline uint64_t ReadUInt64(const uint8_t *data, uint8_t byte_width) +{ +#if defined(_MSC_VER) && ((defined(_M_X64) && !defined(_M_ARM64EC)) || defined _M_IX86) + uint64_t u = 0; + __movsb(reinterpret_cast(&u), reinterpret_cast(data), byte_width); + return flatbuffers::EndianScalar(u); +#else + return ReadSizedScalar(data, byte_width); +#endif + // clang-format on +} + +inline double ReadDouble(const uint8_t *data, uint8_t byte_width) +{ + return ReadSizedScalar(data, byte_width); +} + +inline const uint8_t *Indirect(const uint8_t *offset, uint8_t byte_width) +{ + return offset - ReadUInt64(offset, byte_width); +} + +template const uint8_t *Indirect(const uint8_t *offset) +{ + return offset - flatbuffers::ReadScalar(offset); +} + +inline BitWidth WidthU(uint64_t u) +{ +#define FLATBUFFERS_GET_FIELD_BIT_WIDTH(value, width) \ + { \ + if (!((u) & ~((1ULL << (width)) - 1ULL))) \ + return BIT_WIDTH_##width; \ + } + FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 8); + FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 16); + FLATBUFFERS_GET_FIELD_BIT_WIDTH(u, 32); +#undef FLATBUFFERS_GET_FIELD_BIT_WIDTH + return BIT_WIDTH_64; +} + +inline BitWidth WidthI(int64_t i) +{ + auto u = static_cast(i) << 1; + return WidthU(i >= 0 ? u : ~u); +} + +inline BitWidth WidthF(double f) +{ + return static_cast(static_cast(f)) == f ? BIT_WIDTH_32 : BIT_WIDTH_64; +} + +// Base class of all types below. +// Points into the data buffer and allows access to one type. +class Object +{ +public: + Object(const uint8_t *data, uint8_t byte_width) : data_(data), byte_width_(byte_width) {} + +protected: + const uint8_t *data_; + uint8_t byte_width_; +}; + +// Object that has a size, obtained either from size prefix, or elsewhere. +class Sized : public Object +{ +public: + // Size prefix. + Sized(const uint8_t *data, uint8_t byte_width) : Object(data, byte_width), size_(read_size()) {} + // Manual size. + Sized(const uint8_t *data, uint8_t byte_width, size_t sz) : Object(data, byte_width), size_(sz) {} + size_t size() const { return size_; } + // Access size stored in `byte_width_` bytes before data_ pointer. + size_t read_size() const + { + return static_cast(ReadUInt64(data_ - byte_width_, byte_width_)); + } + +protected: + size_t size_; +}; + +class String : public Sized +{ +public: + // Size prefix. + String(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} + // Manual size. + String(const uint8_t *data, uint8_t byte_width, size_t sz) : Sized(data, byte_width, sz) {} + + size_t length() const { return size(); } + const char *c_str() const { return reinterpret_cast(data_); } + std::string str() const { return std::string(c_str(), size()); } + + static String EmptyString() + { + static const char *empty_string = ""; + return String(reinterpret_cast(empty_string), 1, 0); + } + bool IsTheEmptyString() const { return data_ == EmptyString().data_; } +}; + +class Blob : public Sized +{ +public: + Blob(const uint8_t *data_buf, uint8_t byte_width) : Sized(data_buf, byte_width) {} + + static Blob EmptyBlob() + { + static const uint8_t empty_blob[] = {0 /*len*/}; + return Blob(empty_blob + 1, 1); + } + bool IsTheEmptyBlob() const { return data_ == EmptyBlob().data_; } + const uint8_t *data() const { return data_; } +}; + +class Vector : public Sized +{ +public: + Vector(const uint8_t *data, uint8_t byte_width) : Sized(data, byte_width) {} + + Reference operator[](size_t i) const; + + static Vector EmptyVector() + { + static const uint8_t empty_vector[] = {0 /*len*/}; + return Vector(empty_vector + 1, 1); + } + bool IsTheEmptyVector() const { return data_ == EmptyVector().data_; } +}; + +class TypedVector : public Sized +{ +public: + TypedVector(const uint8_t *data, uint8_t byte_width, Type element_type) + : Sized(data, byte_width), type_(element_type) + { + } + + Reference operator[](size_t i) const; + + static TypedVector EmptyTypedVector() + { + static const uint8_t empty_typed_vector[] = {0 /*len*/}; + return TypedVector(empty_typed_vector + 1, 1, FBT_INT); + } + bool IsTheEmptyVector() const { return data_ == TypedVector::EmptyTypedVector().data_; } + + Type ElementType() { return type_; } + + friend Reference; + +private: + Type type_; + + friend Map; +}; + +class FixedTypedVector : public Object +{ +public: + FixedTypedVector(const uint8_t *data, uint8_t byte_width, Type element_type, uint8_t len) + : Object(data, byte_width), type_(element_type), len_(len) + { + } + + Reference operator[](size_t i) const; + + static FixedTypedVector EmptyFixedTypedVector() + { + static const uint8_t fixed_empty_vector[] = {0 /* unused */}; + return FixedTypedVector(fixed_empty_vector, 1, FBT_INT, 0); + } + bool IsTheEmptyFixedTypedVector() const + { + return data_ == FixedTypedVector::EmptyFixedTypedVector().data_; + } + + Type ElementType() { return type_; } + uint8_t size() { return len_; } + +private: + Type type_; + uint8_t len_; +}; + +class Map : public Vector +{ +public: + Map(const uint8_t *data, uint8_t byte_width) : Vector(data, byte_width) {} + + Reference operator[](const char *key) const; + Reference operator[](const std::string &key) const; + + Vector Values() const { return Vector(data_, byte_width_); } + + TypedVector Keys() const + { + const size_t num_prefixed_fields = 3; + auto keys_offset = data_ - byte_width_ * num_prefixed_fields; + return TypedVector(Indirect(keys_offset, byte_width_), + static_cast(ReadUInt64(keys_offset + byte_width_, byte_width_)), + FBT_KEY); + } + + static Map EmptyMap() + { + static const uint8_t empty_map[] = { + 0 /*keys_len*/, 0 /*keys_offset*/, 1 /*keys_width*/, 0 /*len*/ + }; + return Map(empty_map + 4, 1); + } + + bool IsTheEmptyMap() const { return data_ == EmptyMap().data_; } +}; + +template void AppendToString(std::string &s, T &&v, bool keys_quoted) +{ + s += "[ "; + for (size_t i = 0; i < v.size(); i++) + { + if (i) + s += ", "; + v[i].ToString(true, keys_quoted, s); + } + s += " ]"; +} + +class Reference +{ +public: + Reference() : data_(nullptr), parent_width_(0), byte_width_(BIT_WIDTH_8), type_(FBT_NULL) {} + + Reference(const uint8_t *data, uint8_t parent_width, uint8_t byte_width, Type type) + : data_(data), parent_width_(parent_width), byte_width_(byte_width), type_(type) + { + } + + Reference(const uint8_t *data, uint8_t parent_width, uint8_t packed_type) + : data_(data), parent_width_(parent_width) + { + byte_width_ = 1U << static_cast(packed_type & 3); + type_ = static_cast(packed_type >> 2); + } + + Type GetType() const { return type_; } + + bool IsNull() const { return type_ == FBT_NULL; } + bool IsBool() const { return type_ == FBT_BOOL; } + bool IsInt() const { return type_ == FBT_INT || type_ == FBT_INDIRECT_INT; } + bool IsUInt() const { return type_ == FBT_UINT || type_ == FBT_INDIRECT_UINT; } + bool IsIntOrUint() const { return IsInt() || IsUInt(); } + bool IsFloat() const { return type_ == FBT_FLOAT || type_ == FBT_INDIRECT_FLOAT; } + bool IsNumeric() const { return IsIntOrUint() || IsFloat(); } + bool IsString() const { return type_ == FBT_STRING; } + bool IsKey() const { return type_ == FBT_KEY; } + bool IsVector() const { return type_ == FBT_VECTOR || type_ == FBT_MAP; } + bool IsUntypedVector() const { return type_ == FBT_VECTOR; } + bool IsTypedVector() const { return flexbuffers::IsTypedVector(type_); } + bool IsFixedTypedVector() const { return flexbuffers::IsFixedTypedVector(type_); } + bool IsAnyVector() const { return (IsTypedVector() || IsFixedTypedVector() || IsVector()); } + bool IsMap() const { return type_ == FBT_MAP; } + bool IsBlob() const { return type_ == FBT_BLOB; } + bool AsBool() const + { + return (type_ == FBT_BOOL ? ReadUInt64(data_, parent_width_) : AsUInt64()) != 0; + } + + // Reads any type as a int64_t. Never fails, does most sensible conversion. + // Truncates floats, strings are attempted to be parsed for a number, + // vectors/maps return their size. Returns 0 if all else fails. + int64_t AsInt64() const + { + if (type_ == FBT_INT) + { + // A fast path for the common case. + return ReadInt64(data_, parent_width_); + } + else + switch (type_) + { + case FBT_INDIRECT_INT: + return ReadInt64(Indirect(), byte_width_); + case FBT_UINT: + return ReadUInt64(data_, parent_width_); + case FBT_INDIRECT_UINT: + return ReadUInt64(Indirect(), byte_width_); + case FBT_FLOAT: + return static_cast(ReadDouble(data_, parent_width_)); + case FBT_INDIRECT_FLOAT: + return static_cast(ReadDouble(Indirect(), byte_width_)); + case FBT_NULL: + return 0; + case FBT_STRING: + return flatbuffers::StringToInt(AsString().c_str()); + case FBT_VECTOR: + return static_cast(AsVector().size()); + case FBT_BOOL: + return ReadInt64(data_, parent_width_); + default: + // Convert other things to int. + return 0; + } + } + + // TODO: could specialize these to not use AsInt64() if that saves + // extension ops in generated code, and use a faster op than ReadInt64. + int32_t AsInt32() const { return static_cast(AsInt64()); } + int16_t AsInt16() const { return static_cast(AsInt64()); } + int8_t AsInt8() const { return static_cast(AsInt64()); } + + uint64_t AsUInt64() const + { + if (type_ == FBT_UINT) + { + // A fast path for the common case. + return ReadUInt64(data_, parent_width_); + } + else + switch (type_) + { + case FBT_INDIRECT_UINT: + return ReadUInt64(Indirect(), byte_width_); + case FBT_INT: + return ReadInt64(data_, parent_width_); + case FBT_INDIRECT_INT: + return ReadInt64(Indirect(), byte_width_); + case FBT_FLOAT: + return static_cast(ReadDouble(data_, parent_width_)); + case FBT_INDIRECT_FLOAT: + return static_cast(ReadDouble(Indirect(), byte_width_)); + case FBT_NULL: + return 0; + case FBT_STRING: + return flatbuffers::StringToUInt(AsString().c_str()); + case FBT_VECTOR: + return static_cast(AsVector().size()); + case FBT_BOOL: + return ReadUInt64(data_, parent_width_); + default: + // Convert other things to uint. + return 0; + } + } + + uint32_t AsUInt32() const { return static_cast(AsUInt64()); } + uint16_t AsUInt16() const { return static_cast(AsUInt64()); } + uint8_t AsUInt8() const { return static_cast(AsUInt64()); } + + double AsDouble() const + { + if (type_ == FBT_FLOAT) + { + // A fast path for the common case. + return ReadDouble(data_, parent_width_); + } + else + switch (type_) + { + case FBT_INDIRECT_FLOAT: + return ReadDouble(Indirect(), byte_width_); + case FBT_INT: + return static_cast(ReadInt64(data_, parent_width_)); + case FBT_UINT: + return static_cast(ReadUInt64(data_, parent_width_)); + case FBT_INDIRECT_INT: + return static_cast(ReadInt64(Indirect(), byte_width_)); + case FBT_INDIRECT_UINT: + return static_cast(ReadUInt64(Indirect(), byte_width_)); + case FBT_NULL: + return 0.0; + case FBT_STRING: + { + double d; + flatbuffers::StringToNumber(AsString().c_str(), &d); + return d; + } + case FBT_VECTOR: + return static_cast(AsVector().size()); + case FBT_BOOL: + return static_cast(ReadUInt64(data_, parent_width_)); + default: + // Convert strings and other things to float. + return 0; + } + } + + float AsFloat() const { return static_cast(AsDouble()); } + + const char *AsKey() const + { + if (type_ == FBT_KEY || type_ == FBT_STRING) + { + return reinterpret_cast(Indirect()); + } + else + { + return ""; + } + } + + // This function returns the empty string if you try to read something that + // is not a string or key. + String AsString() const + { + if (type_ == FBT_STRING) + { + return String(Indirect(), byte_width_); + } + else if (type_ == FBT_KEY) + { + auto key = Indirect(); + return String(key, byte_width_, strlen(reinterpret_cast(key))); + } + else + { + return String::EmptyString(); + } + } + + // Unlike AsString(), this will convert any type to a std::string. + std::string ToString() const + { + std::string s; + ToString(false, false, s); + return s; + } + + // Convert any type to a JSON-like string. strings_quoted determines if + // string values at the top level receive "" quotes (inside other values + // they always do). keys_quoted determines if keys are quoted, at any level. + // TODO(wvo): add further options to have indentation/newlines. + void ToString(bool strings_quoted, bool keys_quoted, std::string &s) const + { + if (type_ == FBT_STRING) + { + String str(Indirect(), byte_width_); + if (strings_quoted) + { + flatbuffers::EscapeString(str.c_str(), str.length(), &s, true, false); + } + else + { + s.append(str.c_str(), str.length()); + } + } + else if (IsKey()) + { + auto str = AsKey(); + if (keys_quoted) + { + flatbuffers::EscapeString(str, strlen(str), &s, true, false); + } + else + { + s += str; + } + } + else if (IsInt()) + { + s += flatbuffers::NumToString(AsInt64()); + } + else if (IsUInt()) + { + s += flatbuffers::NumToString(AsUInt64()); + } + else if (IsFloat()) + { + s += flatbuffers::NumToString(AsDouble()); + } + else if (IsNull()) + { + s += "null"; + } + else if (IsBool()) + { + s += AsBool() ? "true" : "false"; + } + else if (IsMap()) + { + s += "{ "; + auto m = AsMap(); + auto keys = m.Keys(); + auto vals = m.Values(); + for (size_t i = 0; i < keys.size(); i++) + { + keys[i].ToString(true, keys_quoted, s); + s += ": "; + vals[i].ToString(true, keys_quoted, s); + if (i < keys.size() - 1) + s += ", "; + } + s += " }"; + } + else if (IsVector()) + { + AppendToString(s, AsVector(), keys_quoted); + } + else if (IsTypedVector()) + { + AppendToString(s, AsTypedVector(), keys_quoted); + } + else if (IsFixedTypedVector()) + { + AppendToString(s, AsFixedTypedVector(), keys_quoted); + } + else if (IsBlob()) + { + auto blob = AsBlob(); + flatbuffers::EscapeString(reinterpret_cast(blob.data()), blob.size(), &s, true, + false); + } + else + { + s += "(?)"; + } + } + + // This function returns the empty blob if you try to read a not-blob. + // Strings can be viewed as blobs too. + Blob AsBlob() const + { + if (type_ == FBT_BLOB || type_ == FBT_STRING) + { + return Blob(Indirect(), byte_width_); + } + else + { + return Blob::EmptyBlob(); + } + } + + // This function returns the empty vector if you try to read a not-vector. + // Maps can be viewed as vectors too. + Vector AsVector() const + { + if (type_ == FBT_VECTOR || type_ == FBT_MAP) + { + return Vector(Indirect(), byte_width_); + } + else + { + return Vector::EmptyVector(); + } + } + + TypedVector AsTypedVector() const + { + if (IsTypedVector()) + { + auto tv = TypedVector(Indirect(), byte_width_, ToTypedVectorElementType(type_)); + if (tv.type_ == FBT_STRING) + { + // These can't be accessed as strings, since we don't know the bit-width + // of the size field, see the declaration of + // FBT_VECTOR_STRING_DEPRECATED above for details. + // We change the type here to be keys, which are a subtype of strings, + // and will ignore the size field. This will truncate strings with + // embedded nulls. + tv.type_ = FBT_KEY; + } + return tv; + } + else + { + return TypedVector::EmptyTypedVector(); + } + } + + FixedTypedVector AsFixedTypedVector() const + { + if (IsFixedTypedVector()) + { + uint8_t len = 0; + auto vtype = ToFixedTypedVectorElementType(type_, &len); + return FixedTypedVector(Indirect(), byte_width_, vtype, len); + } + else + { + return FixedTypedVector::EmptyFixedTypedVector(); + } + } + + Map AsMap() const + { + if (type_ == FBT_MAP) + { + return Map(Indirect(), byte_width_); + } + else + { + return Map::EmptyMap(); + } + } + + template T As() const; + + // Experimental: Mutation functions. + // These allow scalars in an already created buffer to be updated in-place. + // Since by default scalars are stored in the smallest possible space, + // the new value may not fit, in which case these functions return false. + // To avoid this, you can construct the values you intend to mutate using + // Builder::ForceMinimumBitWidth. + bool MutateInt(int64_t i) + { + if (type_ == FBT_INT) + { + return Mutate(data_, i, parent_width_, WidthI(i)); + } + else if (type_ == FBT_INDIRECT_INT) + { + return Mutate(Indirect(), i, byte_width_, WidthI(i)); + } + else if (type_ == FBT_UINT) + { + auto u = static_cast(i); + return Mutate(data_, u, parent_width_, WidthU(u)); + } + else if (type_ == FBT_INDIRECT_UINT) + { + auto u = static_cast(i); + return Mutate(Indirect(), u, byte_width_, WidthU(u)); + } + else + { + return false; + } + } + + bool MutateBool(bool b) + { + return type_ == FBT_BOOL && Mutate(data_, b, parent_width_, BIT_WIDTH_8); + } + + bool MutateUInt(uint64_t u) + { + if (type_ == FBT_UINT) + { + return Mutate(data_, u, parent_width_, WidthU(u)); + } + else if (type_ == FBT_INDIRECT_UINT) + { + return Mutate(Indirect(), u, byte_width_, WidthU(u)); + } + else if (type_ == FBT_INT) + { + auto i = static_cast(u); + return Mutate(data_, i, parent_width_, WidthI(i)); + } + else if (type_ == FBT_INDIRECT_INT) + { + auto i = static_cast(u); + return Mutate(Indirect(), i, byte_width_, WidthI(i)); + } + else + { + return false; + } + } + + bool MutateFloat(float f) + { + if (type_ == FBT_FLOAT) + { + return MutateF(data_, f, parent_width_, BIT_WIDTH_32); + } + else if (type_ == FBT_INDIRECT_FLOAT) + { + return MutateF(Indirect(), f, byte_width_, BIT_WIDTH_32); + } + else + { + return false; + } + } + + bool MutateFloat(double d) + { + if (type_ == FBT_FLOAT) + { + return MutateF(data_, d, parent_width_, WidthF(d)); + } + else if (type_ == FBT_INDIRECT_FLOAT) + { + return MutateF(Indirect(), d, byte_width_, WidthF(d)); + } + else + { + return false; + } + } + + bool MutateString(const char *str, size_t len) + { + auto s = AsString(); + if (s.IsTheEmptyString()) + return false; + // This is very strict, could allow shorter strings, but that creates + // garbage. + if (s.length() != len) + return false; + memcpy(const_cast(s.c_str()), str, len); + return true; + } + bool MutateString(const char *str) { return MutateString(str, strlen(str)); } + bool MutateString(const std::string &str) { return MutateString(str.data(), str.length()); } + +private: + const uint8_t *Indirect() const { return flexbuffers::Indirect(data_, parent_width_); } + + template + bool Mutate(const uint8_t *dest, T t, size_t byte_width, BitWidth value_width) + { + auto fits = static_cast(static_cast(1U) << value_width) <= byte_width; + if (fits) + { + t = flatbuffers::EndianScalar(t); + memcpy(const_cast(dest), &t, byte_width); + } + return fits; + } + + template + bool MutateF(const uint8_t *dest, T t, size_t byte_width, BitWidth value_width) + { + if (byte_width == sizeof(double)) + return Mutate(dest, static_cast(t), byte_width, value_width); + if (byte_width == sizeof(float)) + return Mutate(dest, static_cast(t), byte_width, value_width); + FLATBUFFERS_ASSERT(false); + return false; + } + + const uint8_t *data_; + uint8_t parent_width_; + uint8_t byte_width_; + Type type_; +}; + +// Template specialization for As(). +template <> inline bool Reference::As() const { return AsBool(); } + +template <> inline int8_t Reference::As() const { return AsInt8(); } +template <> inline int16_t Reference::As() const { return AsInt16(); } +template <> inline int32_t Reference::As() const { return AsInt32(); } +template <> inline int64_t Reference::As() const { return AsInt64(); } + +template <> inline uint8_t Reference::As() const { return AsUInt8(); } +template <> inline uint16_t Reference::As() const { return AsUInt16(); } +template <> inline uint32_t Reference::As() const { return AsUInt32(); } +template <> inline uint64_t Reference::As() const { return AsUInt64(); } + +template <> inline double Reference::As() const { return AsDouble(); } +template <> inline float Reference::As() const { return AsFloat(); } + +template <> inline String Reference::As() const { return AsString(); } +template <> inline std::string Reference::As() const { return AsString().str(); } + +template <> inline Blob Reference::As() const { return AsBlob(); } +template <> inline Vector Reference::As() const { return AsVector(); } +template <> inline TypedVector Reference::As() const { return AsTypedVector(); } +template <> inline FixedTypedVector Reference::As() const +{ + return AsFixedTypedVector(); +} +template <> inline Map Reference::As() const { return AsMap(); } + +inline uint8_t PackedType(BitWidth bit_width, Type type) +{ + return static_cast(bit_width | (type << 2)); +} + +inline uint8_t NullPackedType() { return PackedType(BIT_WIDTH_8, FBT_NULL); } + +// Vector accessors. +// Note: if you try to access outside of bounds, you get a Null value back +// instead. Normally this would be an assert, but since this is "dynamically +// typed" data, you may not want that (someone sends you a 2d vector and you +// wanted 3d). +// The Null converts seamlessly into a default value for any other type. +// TODO(wvo): Could introduce an #ifdef that makes this into an assert? +inline Reference Vector::operator[](size_t i) const +{ + auto len = size(); + if (i >= len) + return Reference(nullptr, 1, NullPackedType()); + auto packed_type = (data_ + len * byte_width_)[i]; + auto elem = data_ + i * byte_width_; + return Reference(elem, byte_width_, packed_type); +} + +inline Reference TypedVector::operator[](size_t i) const +{ + auto len = size(); + if (i >= len) + return Reference(nullptr, 1, NullPackedType()); + auto elem = data_ + i * byte_width_; + return Reference(elem, byte_width_, 1, type_); +} + +inline Reference FixedTypedVector::operator[](size_t i) const +{ + if (i >= len_) + return Reference(nullptr, 1, NullPackedType()); + auto elem = data_ + i * byte_width_; + return Reference(elem, byte_width_, 1, type_); +} + +template int KeyCompare(const void *key, const void *elem) +{ + auto str_elem = + reinterpret_cast(Indirect(reinterpret_cast(elem))); + auto skey = reinterpret_cast(key); + return strcmp(skey, str_elem); +} + +inline Reference Map::operator[](const char *key) const +{ + auto keys = Keys(); + // We can't pass keys.byte_width_ to the comparison function, so we have + // to pick the right one ahead of time. + int (*comp)(const void *, const void *) = nullptr; + switch (keys.byte_width_) + { + case 1: + comp = KeyCompare; + break; + case 2: + comp = KeyCompare; + break; + case 4: + comp = KeyCompare; + break; + case 8: + comp = KeyCompare; + break; + } + auto res = std::bsearch(key, keys.data_, keys.size(), keys.byte_width_, comp); + if (!res) + return Reference(nullptr, 1, NullPackedType()); + auto i = (reinterpret_cast(res) - keys.data_) / keys.byte_width_; + return (*static_cast(this))[i]; +} + +inline Reference Map::operator[](const std::string &key) const { return (*this)[key.c_str()]; } + +inline Reference GetRoot(const uint8_t *buffer, size_t size) +{ + // See Finish() below for the serialization counterpart of this. + // The root starts at the end of the buffer, so we parse backwards from there. + auto end = buffer + size; + auto byte_width = *--end; + auto packed_type = *--end; + end -= byte_width; // The root data item. + return Reference(end, byte_width, packed_type); +} + +inline Reference GetRoot(const std::vector &buffer) +{ + return GetRoot(flatbuffers::vector_data(buffer), buffer.size()); +} + +// Flags that configure how the Builder behaves. +// The "Share" flags determine if the Builder automatically tries to pool +// this type. Pooling can reduce the size of serialized data if there are +// multiple maps of the same kind, at the expense of slightly slower +// serialization (the cost of lookups) and more memory use (std::set). +// By default this is on for keys, but off for strings. +// Turn keys off if you have e.g. only one map. +// Turn strings on if you expect many non-unique string values. +// Additionally, sharing key vectors can save space if you have maps with +// identical field populations. +enum BuilderFlag +{ + BUILDER_FLAG_NONE = 0, + BUILDER_FLAG_SHARE_KEYS = 1, + BUILDER_FLAG_SHARE_STRINGS = 2, + BUILDER_FLAG_SHARE_KEYS_AND_STRINGS = 3, + BUILDER_FLAG_SHARE_KEY_VECTORS = 4, + BUILDER_FLAG_SHARE_ALL = 7, +}; + +class Builder FLATBUFFERS_FINAL_CLASS +{ +public: + Builder(size_t initial_size = 256, BuilderFlag flags = BUILDER_FLAG_SHARE_KEYS) + : buf_(initial_size), finished_(false), has_duplicate_keys_(false), flags_(flags), + force_min_bit_width_(BIT_WIDTH_8), key_pool(KeyOffsetCompare(buf_)), + string_pool(StringOffsetCompare(buf_)) + { + buf_.clear(); + } + +#ifdef FLATBUFFERS_DEFAULT_DECLARATION + Builder(Builder &&) = default; + Builder &operator=(Builder &&) = default; +#endif + + /// @brief Get the serialized buffer (after you call `Finish()`). + /// @return Returns a vector owned by this class. + const std::vector &GetBuffer() const + { + Finished(); + return buf_; + } + + // Size of the buffer. Does not include unfinished values. + size_t GetSize() const { return buf_.size(); } + + // Reset all state so we can re-use the buffer. + void Clear() + { + buf_.clear(); + stack_.clear(); + finished_ = false; + // flags_ remains as-is; + force_min_bit_width_ = BIT_WIDTH_8; + key_pool.clear(); + string_pool.clear(); + } + + // All value constructing functions below have two versions: one that + // takes a key (for placement inside a map) and one that doesn't (for inside + // vectors and elsewhere). + + void Null() { stack_.push_back(Value()); } + void Null(const char *key) + { + Key(key); + Null(); + } + + void Int(int64_t i) { stack_.push_back(Value(i, FBT_INT, WidthI(i))); } + void Int(const char *key, int64_t i) + { + Key(key); + Int(i); + } + + void UInt(uint64_t u) { stack_.push_back(Value(u, FBT_UINT, WidthU(u))); } + void UInt(const char *key, uint64_t u) + { + Key(key); + UInt(u); + } + + void Float(float f) { stack_.push_back(Value(f)); } + void Float(const char *key, float f) + { + Key(key); + Float(f); + } + + void Double(double f) { stack_.push_back(Value(f)); } + void Double(const char *key, double d) + { + Key(key); + Double(d); + } + + void Bool(bool b) { stack_.push_back(Value(b)); } + void Bool(const char *key, bool b) + { + Key(key); + Bool(b); + } + + void IndirectInt(int64_t i) { PushIndirect(i, FBT_INDIRECT_INT, WidthI(i)); } + void IndirectInt(const char *key, int64_t i) + { + Key(key); + IndirectInt(i); + } + + void IndirectUInt(uint64_t u) { PushIndirect(u, FBT_INDIRECT_UINT, WidthU(u)); } + void IndirectUInt(const char *key, uint64_t u) + { + Key(key); + IndirectUInt(u); + } + + void IndirectFloat(float f) { PushIndirect(f, FBT_INDIRECT_FLOAT, BIT_WIDTH_32); } + void IndirectFloat(const char *key, float f) + { + Key(key); + IndirectFloat(f); + } + + void IndirectDouble(double f) { PushIndirect(f, FBT_INDIRECT_FLOAT, WidthF(f)); } + void IndirectDouble(const char *key, double d) + { + Key(key); + IndirectDouble(d); + } + + size_t Key(const char *str, size_t len) + { + auto sloc = buf_.size(); + WriteBytes(str, len + 1); + if (flags_ & BUILDER_FLAG_SHARE_KEYS) + { + auto it = key_pool.find(sloc); + if (it != key_pool.end()) + { + // Already in the buffer. Remove key we just serialized, and use + // existing offset instead. + buf_.resize(sloc); + sloc = *it; + } + else + { + key_pool.insert(sloc); + } + } + stack_.push_back(Value(static_cast(sloc), FBT_KEY, BIT_WIDTH_8)); + return sloc; + } + + size_t Key(const char *str) { return Key(str, strlen(str)); } + size_t Key(const std::string &str) { return Key(str.c_str(), str.size()); } + + size_t String(const char *str, size_t len) + { + auto reset_to = buf_.size(); + auto sloc = CreateBlob(str, len, 1, FBT_STRING); + if (flags_ & BUILDER_FLAG_SHARE_STRINGS) + { + StringOffset so(sloc, len); + auto it = string_pool.find(so); + if (it != string_pool.end()) + { + // Already in the buffer. Remove string we just serialized, and use + // existing offset instead. + buf_.resize(reset_to); + sloc = it->first; + stack_.back().u_ = sloc; + } + else + { + string_pool.insert(so); + } + } + return sloc; + } + size_t String(const char *str) { return String(str, strlen(str)); } + size_t String(const std::string &str) { return String(str.c_str(), str.size()); } + void String(const flexbuffers::String &str) { String(str.c_str(), str.length()); } + + void String(const char *key, const char *str) + { + Key(key); + String(str); + } + void String(const char *key, const std::string &str) + { + Key(key); + String(str); + } + void String(const char *key, const flexbuffers::String &str) + { + Key(key); + String(str); + } + + size_t Blob(const void *data, size_t len) { return CreateBlob(data, len, 0, FBT_BLOB); } + size_t Blob(const std::vector &v) + { + return CreateBlob(flatbuffers::vector_data(v), v.size(), 0, FBT_BLOB); + } + + // TODO(wvo): support all the FlexBuffer types (like flexbuffers::String), + // e.g. Vector etc. Also in overloaded versions. + // Also some FlatBuffers types? + + size_t StartVector() { return stack_.size(); } + size_t StartVector(const char *key) + { + Key(key); + return stack_.size(); + } + size_t StartMap() { return stack_.size(); } + size_t StartMap(const char *key) + { + Key(key); + return stack_.size(); + } + + // TODO(wvo): allow this to specify an aligment greater than the natural + // alignment. + size_t EndVector(size_t start, bool typed, bool fixed) + { + auto vec = CreateVector(start, stack_.size() - start, 1, typed, fixed); + // Remove temp elements and return vector. + stack_.resize(start); + stack_.push_back(vec); + return static_cast(vec.u_); + } + + size_t EndMap(size_t start) + { + // We should have interleaved keys and values on the stack. + // Make sure it is an even number: + auto len = stack_.size() - start; + FLATBUFFERS_ASSERT(!(len & 1)); + len /= 2; + // Make sure keys are all strings: + for (auto key = start; key < stack_.size(); key += 2) + { + FLATBUFFERS_ASSERT(stack_[key].type_ == FBT_KEY); + } + // Now sort values, so later we can do a binary search lookup. + // We want to sort 2 array elements at a time. + struct TwoValue + { + Value key; + Value val; + }; + // TODO(wvo): strict aliasing? + // TODO(wvo): allow the caller to indicate the data is already sorted + // for maximum efficiency? With an assert to check sortedness to make sure + // we're not breaking binary search. + // Or, we can track if the map is sorted as keys are added which would be + // be quite cheap (cheaper than checking it here), so we can skip this + // step automatically when appliccable, and encourage people to write in + // sorted fashion. + // std::sort is typically already a lot faster on sorted data though. + auto dict = reinterpret_cast(flatbuffers::vector_data(stack_) + start); + std::sort(dict, dict + len, [&](const TwoValue &a, const TwoValue &b) -> bool { + auto as = reinterpret_cast(flatbuffers::vector_data(buf_) + a.key.u_); + auto bs = reinterpret_cast(flatbuffers::vector_data(buf_) + b.key.u_); + auto comp = strcmp(as, bs); + // We want to disallow duplicate keys, since this results in a + // map where values cannot be found. + // But we can't assert here (since we don't want to fail on + // random JSON input) or have an error mechanism. + // Instead, we set has_duplicate_keys_ in the builder to + // signal this. + // TODO: Have to check for pointer equality, as some sort + // implementation apparently call this function with the same + // element?? Why? + if (!comp && &a != &b) + has_duplicate_keys_ = true; + return comp < 0; + }); + // First create a vector out of all keys. + // TODO(wvo): if kBuilderFlagShareKeyVectors is true, see if we can share + // the first vector. + auto keys = CreateVector(start, len, 2, true, false); + auto vec = CreateVector(start + 1, len, 2, false, false, &keys); + // Remove temp elements and return map. + stack_.resize(start); + stack_.push_back(vec); + return static_cast(vec.u_); + } + + // Call this after EndMap to see if the map had any duplicate keys. + // Any map with such keys won't be able to retrieve all values. + bool HasDuplicateKeys() const { return has_duplicate_keys_; } + + template size_t Vector(F f) + { + auto start = StartVector(); + f(); + return EndVector(start, false, false); + } + template size_t Vector(F f, T &state) + { + auto start = StartVector(); + f(state); + return EndVector(start, false, false); + } + template size_t Vector(const char *key, F f) + { + auto start = StartVector(key); + f(); + return EndVector(start, false, false); + } + template size_t Vector(const char *key, F f, T &state) + { + auto start = StartVector(key); + f(state); + return EndVector(start, false, false); + } + + template void Vector(const T *elems, size_t len) + { + if (flatbuffers::is_scalar::value) + { + // This path should be a lot quicker and use less space. + ScalarVector(elems, len, false); + } + else + { + auto start = StartVector(); + for (size_t i = 0; i < len; i++) + Add(elems[i]); + EndVector(start, false, false); + } + } + template void Vector(const char *key, const T *elems, size_t len) + { + Key(key); + Vector(elems, len); + } + template void Vector(const std::vector &vec) + { + Vector(flatbuffers::vector_data(vec), vec.size()); + } + + template size_t TypedVector(F f) + { + auto start = StartVector(); + f(); + return EndVector(start, true, false); + } + template size_t TypedVector(F f, T &state) + { + auto start = StartVector(); + f(state); + return EndVector(start, true, false); + } + template size_t TypedVector(const char *key, F f) + { + auto start = StartVector(key); + f(); + return EndVector(start, true, false); + } + template size_t TypedVector(const char *key, F f, T &state) + { + auto start = StartVector(key); + f(state); + return EndVector(start, true, false); + } + + template size_t FixedTypedVector(const T *elems, size_t len) + { + // We only support a few fixed vector lengths. Anything bigger use a + // regular typed vector. + FLATBUFFERS_ASSERT(len >= 2 && len <= 4); + // And only scalar values. + static_assert(flatbuffers::is_scalar::value, "Unrelated types"); + return ScalarVector(elems, len, true); + } + + template size_t FixedTypedVector(const char *key, const T *elems, size_t len) + { + Key(key); + return FixedTypedVector(elems, len); + } + + template size_t Map(F f) + { + auto start = StartMap(); + f(); + return EndMap(start); + } + template size_t Map(F f, T &state) + { + auto start = StartMap(); + f(state); + return EndMap(start); + } + template size_t Map(const char *key, F f) + { + auto start = StartMap(key); + f(); + return EndMap(start); + } + template size_t Map(const char *key, F f, T &state) + { + auto start = StartMap(key); + f(state); + return EndMap(start); + } + template void Map(const std::map &map) + { + auto start = StartMap(); + for (auto it = map.begin(); it != map.end(); ++it) + Add(it->first.c_str(), it->second); + EndMap(start); + } + + // If you wish to share a value explicitly (a value not shared automatically + // through one of the BUILDER_FLAG_SHARE_* flags) you can do so with these + // functions. Or if you wish to turn those flags off for performance reasons + // and still do some explicit sharing. For example: + // builder.IndirectDouble(M_PI); + // auto id = builder.LastValue(); // Remember where we stored it. + // .. more code goes here .. + // builder.ReuseValue(id); // Refers to same double by offset. + // LastValue works regardless of whether the value has a key or not. + // Works on any data type. + struct Value; + Value LastValue() { return stack_.back(); } + void ReuseValue(Value v) { stack_.push_back(v); } + void ReuseValue(const char *key, Value v) + { + Key(key); + ReuseValue(v); + } + + // Overloaded Add that tries to call the correct function above. + void Add(int8_t i) { Int(i); } + void Add(int16_t i) { Int(i); } + void Add(int32_t i) { Int(i); } + void Add(int64_t i) { Int(i); } + void Add(uint8_t u) { UInt(u); } + void Add(uint16_t u) { UInt(u); } + void Add(uint32_t u) { UInt(u); } + void Add(uint64_t u) { UInt(u); } + void Add(float f) { Float(f); } + void Add(double d) { Double(d); } + void Add(bool b) { Bool(b); } + void Add(const char *str) { String(str); } + void Add(const std::string &str) { String(str); } + void Add(const flexbuffers::String &str) { String(str); } + + template void Add(const std::vector &vec) { Vector(vec); } + + template void Add(const char *key, const T &t) + { + Key(key); + Add(t); + } + + template void Add(const std::map &map) { Map(map); } + + template void operator+=(const T &t) { Add(t); } + + // This function is useful in combination with the Mutate* functions above. + // It forces elements of vectors and maps to have a minimum size, such that + // they can later be updated without failing. + // Call with no arguments to reset. + void ForceMinimumBitWidth(BitWidth bw = BIT_WIDTH_8) { force_min_bit_width_ = bw; } + + void Finish() + { + // If you hit this assert, you likely have objects that were never included + // in a parent. You need to have exactly one root to finish a buffer. + // Check your Start/End calls are matched, and all objects are inside + // some other object. + FLATBUFFERS_ASSERT(stack_.size() == 1); + + // Write root value. + auto byte_width = Align(stack_[0].ElemWidth(buf_.size(), 0)); + WriteAny(stack_[0], byte_width); + // Write root type. + Write(stack_[0].StoredPackedType(), 1); + // Write root size. Normally determined by parent, but root has no parent :) + Write(byte_width, 1); + + finished_ = true; + } + +private: + void Finished() const + { + // If you get this assert, you're attempting to get access a buffer + // which hasn't been finished yet. Be sure to call + // Builder::Finish with your root object. + FLATBUFFERS_ASSERT(finished_); + } + + // Align to prepare for writing a scalar with a certain size. + uint8_t Align(BitWidth alignment) + { + auto byte_width = 1U << alignment; + buf_.insert(buf_.end(), flatbuffers::PaddingBytes(buf_.size(), byte_width), 0); + return static_cast(byte_width); + } + + void WriteBytes(const void *val, size_t size) + { + buf_.insert(buf_.end(), reinterpret_cast(val), + reinterpret_cast(val) + size); + } + + template void Write(T val, size_t byte_width) + { + FLATBUFFERS_ASSERT(sizeof(T) >= byte_width); + val = flatbuffers::EndianScalar(val); + WriteBytes(&val, byte_width); + } + + void WriteDouble(double f, uint8_t byte_width) + { + switch (byte_width) + { + case 8: + Write(f, byte_width); + break; + case 4: + Write(static_cast(f), byte_width); + break; + // case 2: Write(static_cast(f), byte_width); break; + // case 1: Write(static_cast(f), byte_width); break; + default: + FLATBUFFERS_ASSERT(0); + } + } + + void WriteOffset(uint64_t o, uint8_t byte_width) + { + auto reloff = buf_.size() - o; + FLATBUFFERS_ASSERT(byte_width == 8 || reloff < 1ULL << (byte_width * 8)); + Write(reloff, byte_width); + } + + template void PushIndirect(T val, Type type, BitWidth bit_width) + { + auto byte_width = Align(bit_width); + auto iloc = buf_.size(); + Write(val, byte_width); + stack_.push_back(Value(static_cast(iloc), type, bit_width)); + } + + static BitWidth WidthB(size_t byte_width) + { + switch (byte_width) + { + case 1: + return BIT_WIDTH_8; + case 2: + return BIT_WIDTH_16; + case 4: + return BIT_WIDTH_32; + case 8: + return BIT_WIDTH_64; + default: + FLATBUFFERS_ASSERT(false); + return BIT_WIDTH_64; + } + } + + template static Type GetScalarType() + { + static_assert(flatbuffers::is_scalar::value, "Unrelated types"); + return flatbuffers::is_floating_point::value + ? FBT_FLOAT + : flatbuffers::is_same::value + ? FBT_BOOL + : (flatbuffers::is_unsigned::value ? FBT_UINT : FBT_INT); + } + +public: + // This was really intended to be private, except for LastValue/ReuseValue. + struct Value + { + union { + int64_t i_; + uint64_t u_; + double f_; + }; + + Type type_; + + // For scalars: of itself, for vector: of its elements, for string: length. + BitWidth min_bit_width_; + + Value() : i_(0), type_(FBT_NULL), min_bit_width_(BIT_WIDTH_8) {} + + Value(bool b) : u_(static_cast(b)), type_(FBT_BOOL), min_bit_width_(BIT_WIDTH_8) {} + + Value(int64_t i, Type t, BitWidth bw) : i_(i), type_(t), min_bit_width_(bw) {} + Value(uint64_t u, Type t, BitWidth bw) : u_(u), type_(t), min_bit_width_(bw) {} + + Value(float f) : f_(static_cast(f)), type_(FBT_FLOAT), min_bit_width_(BIT_WIDTH_32) {} + Value(double f) : f_(f), type_(FBT_FLOAT), min_bit_width_(WidthF(f)) {} + + uint8_t StoredPackedType(BitWidth parent_bit_width_ = BIT_WIDTH_8) const + { + return PackedType(StoredWidth(parent_bit_width_), type_); + } + + BitWidth ElemWidth(size_t buf_size, size_t elem_index) const + { + if (IsInline(type_)) + { + return min_bit_width_; + } + else + { + // We have an absolute offset, but want to store a relative offset + // elem_index elements beyond the current buffer end. Since whether + // the relative offset fits in a certain byte_width depends on + // the size of the elements before it (and their alignment), we have + // to test for each size in turn. + for (size_t byte_width = 1; byte_width <= sizeof(flatbuffers::largest_scalar_t); + byte_width *= 2) + { + // Where are we going to write this offset? + auto offset_loc = + buf_size + flatbuffers::PaddingBytes(buf_size, byte_width) + elem_index * byte_width; + // Compute relative offset. + auto offset = offset_loc - u_; + // Does it fit? + auto bit_width = WidthU(offset); + if (static_cast(static_cast(1U) << bit_width) == byte_width) + return bit_width; + } + FLATBUFFERS_ASSERT(false); // Must match one of the sizes above. + return BIT_WIDTH_64; + } + } + + BitWidth StoredWidth(BitWidth parent_bit_width_ = BIT_WIDTH_8) const + { + if (IsInline(type_)) + { + return (std::max)(min_bit_width_, parent_bit_width_); + } + else + { + return min_bit_width_; + } + } + }; + +private: + void WriteAny(const Value &val, uint8_t byte_width) + { + switch (val.type_) + { + case FBT_NULL: + case FBT_INT: + Write(val.i_, byte_width); + break; + case FBT_BOOL: + case FBT_UINT: + Write(val.u_, byte_width); + break; + case FBT_FLOAT: + WriteDouble(val.f_, byte_width); + break; + default: + WriteOffset(val.u_, byte_width); + break; + } + } + + size_t CreateBlob(const void *data, size_t len, size_t trailing, Type type) + { + auto bit_width = WidthU(len); + auto byte_width = Align(bit_width); + Write(len, byte_width); + auto sloc = buf_.size(); + WriteBytes(data, len + trailing); + stack_.push_back(Value(static_cast(sloc), type, bit_width)); + return sloc; + } + + template size_t ScalarVector(const T *elems, size_t len, bool fixed) + { + auto vector_type = GetScalarType(); + auto byte_width = sizeof(T); + auto bit_width = WidthB(byte_width); + // If you get this assert, you're trying to write a vector with a size + // field that is bigger than the scalars you're trying to write (e.g. a + // byte vector > 255 elements). For such types, write a "blob" instead. + // TODO: instead of asserting, could write vector with larger elements + // instead, though that would be wasteful. + FLATBUFFERS_ASSERT(WidthU(len) <= bit_width); + Align(bit_width); + if (!fixed) + Write(len, byte_width); + auto vloc = buf_.size(); + for (size_t i = 0; i < len; i++) + Write(elems[i], byte_width); + stack_.push_back( + Value(static_cast(vloc), ToTypedVector(vector_type, fixed ? len : 0), bit_width)); + return vloc; + } + + Value CreateVector(size_t start, size_t vec_len, size_t step, bool typed, bool fixed, + const Value *keys = nullptr) + { + FLATBUFFERS_ASSERT(!fixed || typed); // typed=false, fixed=true combination is not supported. + // Figure out smallest bit width we can store this vector with. + auto bit_width = (std::max)(force_min_bit_width_, WidthU(vec_len)); + auto prefix_elems = 1; + if (keys) + { + // If this vector is part of a map, we will pre-fix an offset to the keys + // to this vector. + bit_width = (std::max)(bit_width, keys->ElemWidth(buf_.size(), 0)); + prefix_elems += 2; + } + Type vector_type = FBT_KEY; + // Check bit widths and types for all elements. + for (size_t i = start; i < stack_.size(); i += step) + { + auto elem_width = stack_[i].ElemWidth(buf_.size(), i - start + prefix_elems); + bit_width = (std::max)(bit_width, elem_width); + if (typed) + { + if (i == start) + { + vector_type = stack_[i].type_; + } + else + { + // If you get this assert, you are writing a typed vector with + // elements that are not all the same type. + FLATBUFFERS_ASSERT(vector_type == stack_[i].type_); + } + } + } + // If you get this assert, your fixed types are not one of: + // Int / UInt / Float / Key. + FLATBUFFERS_ASSERT(!fixed || IsTypedVectorElementType(vector_type)); + auto byte_width = Align(bit_width); + // Write vector. First the keys width/offset if available, and size. + if (keys) + { + WriteOffset(keys->u_, byte_width); + Write(1ULL << keys->min_bit_width_, byte_width); + } + if (!fixed) + Write(vec_len, byte_width); + // Then the actual data. + auto vloc = buf_.size(); + for (size_t i = start; i < stack_.size(); i += step) + { + WriteAny(stack_[i], byte_width); + } + // Then the types. + if (!typed) + { + for (size_t i = start; i < stack_.size(); i += step) + { + buf_.push_back(stack_[i].StoredPackedType(bit_width)); + } + } + return Value(static_cast(vloc), + keys ? FBT_MAP + : (typed ? ToTypedVector(vector_type, fixed ? vec_len : 0) : FBT_VECTOR), + bit_width); + } + + // You shouldn't really be copying instances of this class. + Builder(const Builder &); + Builder &operator=(const Builder &); + + std::vector buf_; + std::vector stack_; + + bool finished_; + bool has_duplicate_keys_; + + BuilderFlag flags_; + + BitWidth force_min_bit_width_; + + struct KeyOffsetCompare + { + explicit KeyOffsetCompare(const std::vector &buf) : buf_(&buf) {} + bool operator()(size_t a, size_t b) const + { + auto stra = reinterpret_cast(flatbuffers::vector_data(*buf_) + a); + auto strb = reinterpret_cast(flatbuffers::vector_data(*buf_) + b); + return strcmp(stra, strb) < 0; + } + const std::vector *buf_; + }; + + typedef std::pair StringOffset; + struct StringOffsetCompare + { + explicit StringOffsetCompare(const std::vector &buf) : buf_(&buf) {} + bool operator()(const StringOffset &a, const StringOffset &b) const + { + auto stra = reinterpret_cast(flatbuffers::vector_data(*buf_) + a.first); + auto strb = reinterpret_cast(flatbuffers::vector_data(*buf_) + b.first); + return strncmp(stra, strb, (std::min)(a.second, b.second) + 1) < 0; + } + const std::vector *buf_; + }; + + typedef std::set KeyOffsetMap; + typedef std::set StringOffsetMap; + + KeyOffsetMap key_pool; + StringOffsetMap string_pool; +}; + +} // namespace flexbuffers + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // FLATBUFFERS_FLEXBUFFERS_H_ diff --git a/onert-micro/externals/flatbuffers/grpc.h b/onert-micro/externals/flatbuffers/grpc.h new file mode 100644 index 0000000..184c89e --- /dev/null +++ b/onert-micro/externals/flatbuffers/grpc.h @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_GRPC_H_ +#define FLATBUFFERS_GRPC_H_ + +// Helper functionality to glue FlatBuffers and GRPC. + +#include "flatbuffers/flatbuffers.h" +#include "grpc/byte_buffer_reader.h" +#include "grpcpp/support/byte_buffer.h" + +namespace flatbuffers +{ +namespace grpc +{ + +// Message is a typed wrapper around a buffer that manages the underlying +// `grpc_slice` and also provides flatbuffers-specific helpers such as `Verify` +// and `GetRoot`. Since it is backed by a `grpc_slice`, the underlying buffer +// is refcounted and ownership is be managed automatically. +template class Message +{ +public: + Message() : slice_(grpc_empty_slice()) {} + + Message(grpc_slice slice, bool add_ref) : slice_(add_ref ? grpc_slice_ref(slice) : slice) {} + + Message &operator=(const Message &other) = delete; + + Message(Message &&other) : slice_(other.slice_) { other.slice_ = grpc_empty_slice(); } + + Message(const Message &other) = delete; + + Message &operator=(Message &&other) + { + grpc_slice_unref(slice_); + slice_ = other.slice_; + other.slice_ = grpc_empty_slice(); + return *this; + } + + ~Message() { grpc_slice_unref(slice_); } + + const uint8_t *mutable_data() const { return GRPC_SLICE_START_PTR(slice_); } + + const uint8_t *data() const { return GRPC_SLICE_START_PTR(slice_); } + + size_t size() const { return GRPC_SLICE_LENGTH(slice_); } + + bool Verify() const + { + Verifier verifier(data(), size()); + return verifier.VerifyBuffer(nullptr); + } + + T *GetMutableRoot() { return flatbuffers::GetMutableRoot(mutable_data()); } + + const T *GetRoot() const { return flatbuffers::GetRoot(data()); } + + // This is only intended for serializer use, or if you know what you're doing + const grpc_slice &BorrowSlice() const { return slice_; } + +private: + grpc_slice slice_; +}; + +class MessageBuilder; + +// SliceAllocator is a gRPC-specific allocator that uses the `grpc_slice` +// refcounted slices to manage memory ownership. This makes it easy and +// efficient to transfer buffers to gRPC. +class SliceAllocator : public Allocator +{ +public: + SliceAllocator() : slice_(grpc_empty_slice()) {} + + SliceAllocator(const SliceAllocator &other) = delete; + SliceAllocator &operator=(const SliceAllocator &other) = delete; + + SliceAllocator(SliceAllocator &&other) : slice_(grpc_empty_slice()) + { + // default-construct and swap idiom + swap(other); + } + + SliceAllocator &operator=(SliceAllocator &&other) + { + // move-construct and swap idiom + SliceAllocator temp(std::move(other)); + swap(temp); + return *this; + } + + void swap(SliceAllocator &other) + { + using std::swap; + swap(slice_, other.slice_); + } + + virtual ~SliceAllocator() { grpc_slice_unref(slice_); } + + virtual uint8_t *allocate(size_t size) override + { + FLATBUFFERS_ASSERT(GRPC_SLICE_IS_EMPTY(slice_)); + slice_ = grpc_slice_malloc(size); + return GRPC_SLICE_START_PTR(slice_); + } + + virtual void deallocate(uint8_t *p, size_t size) override + { + FLATBUFFERS_ASSERT(p == GRPC_SLICE_START_PTR(slice_)); + FLATBUFFERS_ASSERT(size == GRPC_SLICE_LENGTH(slice_)); + grpc_slice_unref(slice_); + slice_ = grpc_empty_slice(); + } + + virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, size_t new_size, + size_t in_use_back, size_t in_use_front) override + { + FLATBUFFERS_ASSERT(old_p == GRPC_SLICE_START_PTR(slice_)); + FLATBUFFERS_ASSERT(old_size == GRPC_SLICE_LENGTH(slice_)); + FLATBUFFERS_ASSERT(new_size > old_size); + grpc_slice old_slice = slice_; + grpc_slice new_slice = grpc_slice_malloc(new_size); + uint8_t *new_p = GRPC_SLICE_START_PTR(new_slice); + memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, in_use_front); + slice_ = new_slice; + grpc_slice_unref(old_slice); + return new_p; + } + +private: + grpc_slice &get_slice(uint8_t *p, size_t size) + { + FLATBUFFERS_ASSERT(p == GRPC_SLICE_START_PTR(slice_)); + FLATBUFFERS_ASSERT(size == GRPC_SLICE_LENGTH(slice_)); + return slice_; + } + + grpc_slice slice_; + + friend class MessageBuilder; +}; + +// SliceAllocatorMember is a hack to ensure that the MessageBuilder's +// slice_allocator_ member is constructed before the FlatBufferBuilder, since +// the allocator is used in the FlatBufferBuilder ctor. +namespace detail +{ +struct SliceAllocatorMember +{ + SliceAllocator slice_allocator_; +}; +} // namespace detail + +// MessageBuilder is a gRPC-specific FlatBufferBuilder that uses SliceAllocator +// to allocate gRPC buffers. +class MessageBuilder : private detail::SliceAllocatorMember, public FlatBufferBuilder +{ +public: + explicit MessageBuilder(uoffset_t initial_size = 1024) + : FlatBufferBuilder(initial_size, &slice_allocator_, false) + { + } + + MessageBuilder(const MessageBuilder &other) = delete; + MessageBuilder &operator=(const MessageBuilder &other) = delete; + + MessageBuilder(MessageBuilder &&other) : FlatBufferBuilder(1024, &slice_allocator_, false) + { + // Default construct and swap idiom. + Swap(other); + } + + /// Create a MessageBuilder from a FlatBufferBuilder. + explicit MessageBuilder(FlatBufferBuilder &&src, + void (*dealloc)(void *, size_t) = &DefaultAllocator::dealloc) + : FlatBufferBuilder(1024, &slice_allocator_, false) + { + src.Swap(*this); + src.SwapBufAllocator(*this); + if (buf_.capacity()) + { + uint8_t *buf = buf_.scratch_data(); // pointer to memory + size_t capacity = buf_.capacity(); // size of memory + slice_allocator_.slice_ = grpc_slice_new_with_len(buf, capacity, dealloc); + } + else + { + slice_allocator_.slice_ = grpc_empty_slice(); + } + } + + /// Move-assign a FlatBufferBuilder to a MessageBuilder. + /// Only FlatBufferBuilder with default allocator (basically, nullptr) is + /// supported. + MessageBuilder &operator=(FlatBufferBuilder &&src) + { + // Move construct a temporary and swap + MessageBuilder temp(std::move(src)); + Swap(temp); + return *this; + } + + MessageBuilder &operator=(MessageBuilder &&other) + { + // Move construct a temporary and swap + MessageBuilder temp(std::move(other)); + Swap(temp); + return *this; + } + + void Swap(MessageBuilder &other) + { + slice_allocator_.swap(other.slice_allocator_); + FlatBufferBuilder::Swap(other); + // After swapping the FlatBufferBuilder, we swap back the allocator, which + // restores the original allocator back in place. This is necessary because + // MessageBuilder's allocator is its own member (SliceAllocatorMember). The + // allocator passed to FlatBufferBuilder::vector_downward must point to this + // member. + buf_.swap_allocator(other.buf_); + } + + // Releases the ownership of the buffer pointer. + // Returns the size, offset, and the original grpc_slice that + // allocated the buffer. Also see grpc_slice_unref(). + uint8_t *ReleaseRaw(size_t &size, size_t &offset, grpc_slice &slice) + { + uint8_t *buf = FlatBufferBuilder::ReleaseRaw(size, offset); + slice = slice_allocator_.slice_; + slice_allocator_.slice_ = grpc_empty_slice(); + return buf; + } + + ~MessageBuilder() {} + + // GetMessage extracts the subslice of the buffer corresponding to the + // flatbuffers-encoded region and wraps it in a `Message` to handle buffer + // ownership. + template Message GetMessage() + { + auto buf_data = buf_.scratch_data(); // pointer to memory + auto buf_size = buf_.capacity(); // size of memory + auto msg_data = buf_.data(); // pointer to msg + auto msg_size = buf_.size(); // size of msg + // Do some sanity checks on data/size + FLATBUFFERS_ASSERT(msg_data); + FLATBUFFERS_ASSERT(msg_size); + FLATBUFFERS_ASSERT(msg_data >= buf_data); + FLATBUFFERS_ASSERT(msg_data + msg_size <= buf_data + buf_size); + // Calculate offsets from the buffer start + auto begin = msg_data - buf_data; + auto end = begin + msg_size; + // Get the slice we are working with (no refcount change) + grpc_slice slice = slice_allocator_.get_slice(buf_data, buf_size); + // Extract a subslice of the existing slice (increment refcount) + grpc_slice subslice = grpc_slice_sub(slice, begin, end); + // Wrap the subslice in a `Message`, but don't increment refcount + Message msg(subslice, false); + return msg; + } + + template Message ReleaseMessage() + { + Message msg = GetMessage(); + Reset(); + return msg; + } + +private: + // SliceAllocator slice_allocator_; // part of SliceAllocatorMember +}; + +} // namespace grpc +} // namespace flatbuffers + +namespace grpc +{ + +template class SerializationTraits> +{ +public: + static grpc::Status Serialize(const flatbuffers::grpc::Message &msg, grpc_byte_buffer **buffer, + bool *own_buffer) + { + // We are passed in a `Message`, which is a wrapper around a + // `grpc_slice`. We extract it here using `BorrowSlice()`. The const cast + // is necessary because the `grpc_raw_byte_buffer_create` func expects + // non-const slices in order to increment their refcounts. + grpc_slice *slice = const_cast(&msg.BorrowSlice()); + // Now use `grpc_raw_byte_buffer_create` to package the single slice into a + // `grpc_byte_buffer`, incrementing the refcount in the process. + *buffer = grpc_raw_byte_buffer_create(slice, 1); + *own_buffer = true; + return grpc::Status::OK; + } + + // Deserialize by pulling the + static grpc::Status Deserialize(ByteBuffer *buf, flatbuffers::grpc::Message *msg) + { + grpc_byte_buffer *buffer = *reinterpret_cast(buf); + if (!buffer) + { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, "No payload"); + } + // Check if this is a single uncompressed slice. + if ((buffer->type == GRPC_BB_RAW) && (buffer->data.raw.compression == GRPC_COMPRESS_NONE) && + (buffer->data.raw.slice_buffer.count == 1)) + { + // If it is, then we can reference the `grpc_slice` directly. + grpc_slice slice = buffer->data.raw.slice_buffer.slices[0]; + // We wrap a `Message` around the slice, incrementing the refcount. + *msg = flatbuffers::grpc::Message(slice, true); + } + else + { + // Otherwise, we need to use `grpc_byte_buffer_reader_readall` to read + // `buffer` into a single contiguous `grpc_slice`. The gRPC reader gives + // us back a new slice with the refcount already incremented. + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, buffer); + grpc_slice slice = grpc_byte_buffer_reader_readall(&reader); + grpc_byte_buffer_reader_destroy(&reader); + // We wrap a `Message` around the slice, but don't increment refcount + *msg = flatbuffers::grpc::Message(slice, false); + } + grpc_byte_buffer_destroy(buffer); +#if FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION + return ::grpc::Status::OK; +#else + if (msg->Verify()) + { + return ::grpc::Status::OK; + } + else + { + return ::grpc::Status(::grpc::StatusCode::INTERNAL, "Message verification failed"); + } +#endif + } +}; + +} // namespace grpc + +#endif // FLATBUFFERS_GRPC_H_ diff --git a/onert-micro/externals/flatbuffers/hash.h b/onert-micro/externals/flatbuffers/hash.h new file mode 100644 index 0000000..a83c0ff --- /dev/null +++ b/onert-micro/externals/flatbuffers/hash.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_HASH_H_ +#define FLATBUFFERS_HASH_H_ + +#include +#include + +#include "flatbuffers/flatbuffers.h" + +namespace flatbuffers +{ + +template struct FnvTraits +{ + static const T kFnvPrime; + static const T kOffsetBasis; +}; + +template <> struct FnvTraits +{ + static const uint32_t kFnvPrime = 0x01000193; + static const uint32_t kOffsetBasis = 0x811C9DC5; +}; + +template <> struct FnvTraits +{ + static const uint64_t kFnvPrime = 0x00000100000001b3ULL; + static const uint64_t kOffsetBasis = 0xcbf29ce484222645ULL; +}; + +template T HashFnv1(const char *input) +{ + T hash = FnvTraits::kOffsetBasis; + for (const char *c = input; *c; ++c) + { + hash *= FnvTraits::kFnvPrime; + hash ^= static_cast(*c); + } + return hash; +} + +template T HashFnv1a(const char *input) +{ + T hash = FnvTraits::kOffsetBasis; + for (const char *c = input; *c; ++c) + { + hash ^= static_cast(*c); + hash *= FnvTraits::kFnvPrime; + } + return hash; +} + +template <> inline uint16_t HashFnv1(const char *input) +{ + uint32_t hash = HashFnv1(input); + return (hash >> 16) ^ (hash & 0xffff); +} + +template <> inline uint16_t HashFnv1a(const char *input) +{ + uint32_t hash = HashFnv1a(input); + return (hash >> 16) ^ (hash & 0xffff); +} + +template struct NamedHashFunction +{ + const char *name; + + typedef T (*HashFunction)(const char *); + HashFunction function; +}; + +const NamedHashFunction kHashFunctions16[] = { + {"fnv1_16", HashFnv1}, + {"fnv1a_16", HashFnv1a}, +}; + +const NamedHashFunction kHashFunctions32[] = { + {"fnv1_32", HashFnv1}, + {"fnv1a_32", HashFnv1a}, +}; + +const NamedHashFunction kHashFunctions64[] = { + {"fnv1_64", HashFnv1}, + {"fnv1a_64", HashFnv1a}, +}; + +inline NamedHashFunction::HashFunction FindHashFunction16(const char *name) +{ + std::size_t size = sizeof(kHashFunctions16) / sizeof(kHashFunctions16[0]); + for (std::size_t i = 0; i < size; ++i) + { + if (std::strcmp(name, kHashFunctions16[i].name) == 0) + { + return kHashFunctions16[i].function; + } + } + return nullptr; +} + +inline NamedHashFunction::HashFunction FindHashFunction32(const char *name) +{ + std::size_t size = sizeof(kHashFunctions32) / sizeof(kHashFunctions32[0]); + for (std::size_t i = 0; i < size; ++i) + { + if (std::strcmp(name, kHashFunctions32[i].name) == 0) + { + return kHashFunctions32[i].function; + } + } + return nullptr; +} + +inline NamedHashFunction::HashFunction FindHashFunction64(const char *name) +{ + std::size_t size = sizeof(kHashFunctions64) / sizeof(kHashFunctions64[0]); + for (std::size_t i = 0; i < size; ++i) + { + if (std::strcmp(name, kHashFunctions64[i].name) == 0) + { + return kHashFunctions64[i].function; + } + } + return nullptr; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_HASH_H_ diff --git a/onert-micro/externals/flatbuffers/idl.h b/onert-micro/externals/flatbuffers/idl.h new file mode 100644 index 0000000..de0a22a --- /dev/null +++ b/onert-micro/externals/flatbuffers/idl.h @@ -0,0 +1,1145 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_IDL_H_ +#define FLATBUFFERS_IDL_H_ + +#include +#include +#include + +#include "flatbuffers/base.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/flexbuffers.h" +#include "flatbuffers/hash.h" +#include "flatbuffers/reflection.h" + +#if !defined(FLATBUFFERS_CPP98_STL) +#include +#endif // !defined(FLATBUFFERS_CPP98_STL) + +// This file defines the data types representing a parsed IDL (Interface +// Definition Language) / schema file. + +// Limits maximum depth of nested objects. +// Prevents stack overflow while parse scheme, or json, or flexbuffer. +#if !defined(FLATBUFFERS_MAX_PARSING_DEPTH) +#define FLATBUFFERS_MAX_PARSING_DEPTH 64 +#endif + +namespace flatbuffers +{ + +// The order of these matters for Is*() functions below. +// Additionally, Parser::ParseType assumes bool..string is a contiguous range +// of type tokens. +// clang-format off +#define FLATBUFFERS_GEN_TYPES_SCALAR(TD) \ + TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ + TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) /* begin scalar/int */ \ + TD(BOOL, "bool", uint8_t, boolean,bool, bool, bool, bool, Boolean, Bool) \ + TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8, Byte, Int8) \ + TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ + TD(SHORT, "short", int16_t, short, int16, short, int16, i16, Short, Int16) \ + TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16, UShort, UInt16) \ + TD(INT, "int", int32_t, int, int32, int, int32, i32, Int, Int32) \ + TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32, UInt, UInt32) \ + TD(LONG, "long", int64_t, long, int64, long, int64, i64, Long, Int64) \ + TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64, ULong, UInt64) /* end int */ \ + TD(FLOAT, "float", float, float, float32, float, float32, f32, Float, Float32) /* begin float */ \ + TD(DOUBLE, "double", double, double, float64, double, float64, f64, Double, Double) /* end float/scalar */ +#define FLATBUFFERS_GEN_TYPES_POINTER(TD) \ + TD(STRING, "string", Offset, int, int, StringOffset, int, unused, Int, Offset) \ + TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused, Int, Offset) \ + TD(STRUCT, "", Offset, int, int, int, int, unused, Int, Offset) \ + TD(UNION, "", Offset, int, int, int, int, unused, Int, Offset) +#define FLATBUFFERS_GEN_TYPE_ARRAY(TD) \ + TD(ARRAY, "", int, int, int, int, int, unused, Int, Offset) +// The fields are: +// - enum +// - FlatBuffers schema type. +// - C++ type. +// - Java type. +// - Go type. +// - C# / .Net type. +// - Python type. +// - Rust type. +// - Kotlin type. + +// using these macros, we can now write code dealing with types just once, e.g. + +/* +switch (type) { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \ + RTYPE, KTYPE) \ + case BASE_TYPE_ ## ENUM: \ + // do something specific to CTYPE here + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD +} +*/ + +// If not all FLATBUFFERS_GEN_() arguments are necessary for implementation +// of FLATBUFFERS_TD, you can use a variadic macro (with __VA_ARGS__ if needed). +// In the above example, only CTYPE is used to generate the code, it can be rewritten: + +/* +switch (type) { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ + case BASE_TYPE_ ## ENUM: \ + // do something specific to CTYPE here + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD +} +*/ + +#define FLATBUFFERS_GEN_TYPES(TD) \ + FLATBUFFERS_GEN_TYPES_SCALAR(TD) \ + FLATBUFFERS_GEN_TYPES_POINTER(TD) \ + FLATBUFFERS_GEN_TYPE_ARRAY(TD) + +// Create an enum for all the types above. +#ifdef __GNUC__ +__extension__ // Stop GCC complaining about trailing comma with -Wpendantic. +#endif +enum BaseType { + #define FLATBUFFERS_TD(ENUM, ...) \ + BASE_TYPE_ ## ENUM, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD +}; + +#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ + static_assert(sizeof(CTYPE) <= sizeof(largest_scalar_t), \ + "define largest_scalar_t as " #CTYPE); + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) +#undef FLATBUFFERS_TD + +inline bool IsScalar (BaseType t) { return t >= BASE_TYPE_UTYPE && + t <= BASE_TYPE_DOUBLE; } +inline bool IsInteger(BaseType t) { return t >= BASE_TYPE_UTYPE && + t <= BASE_TYPE_ULONG; } +inline bool IsFloat (BaseType t) { return t == BASE_TYPE_FLOAT || + t == BASE_TYPE_DOUBLE; } +inline bool IsLong (BaseType t) { return t == BASE_TYPE_LONG || + t == BASE_TYPE_ULONG; } +inline bool IsBool (BaseType t) { return t == BASE_TYPE_BOOL; } +inline bool IsOneByte(BaseType t) { return t >= BASE_TYPE_UTYPE && + t <= BASE_TYPE_UCHAR; } + +inline bool IsUnsigned(BaseType t) { + return (t == BASE_TYPE_UTYPE) || (t == BASE_TYPE_UCHAR) || + (t == BASE_TYPE_USHORT) || (t == BASE_TYPE_UINT) || + (t == BASE_TYPE_ULONG); +} + +// clang-format on + +extern const char *const kTypeNames[]; +extern const char kTypeSizes[]; + +inline size_t SizeOf(BaseType t) { return kTypeSizes[t]; } + +struct StructDef; +struct EnumDef; +class Parser; + +// Represents any type in the IDL, which is a combination of the BaseType +// and additional information for vectors/structs_. +struct Type +{ + explicit Type(BaseType _base_type = BASE_TYPE_NONE, StructDef *_sd = nullptr, + EnumDef *_ed = nullptr, uint16_t _fixed_length = 0) + : base_type(_base_type), element(BASE_TYPE_NONE), struct_def(_sd), enum_def(_ed), + fixed_length(_fixed_length) + { + } + + bool operator==(const Type &o) + { + return base_type == o.base_type && element == o.element && struct_def == o.struct_def && + enum_def == o.enum_def; + } + + Type VectorType() const { return Type(element, struct_def, enum_def, fixed_length); } + + Offset Serialize(FlatBufferBuilder *builder) const; + + bool Deserialize(const Parser &parser, const reflection::Type *type); + + BaseType base_type; + BaseType element; // only set if t == BASE_TYPE_VECTOR + StructDef *struct_def; // only set if t or element == BASE_TYPE_STRUCT + EnumDef *enum_def; // set if t == BASE_TYPE_UNION / BASE_TYPE_UTYPE, + // or for an integral type derived from an enum. + uint16_t fixed_length; // only set if t == BASE_TYPE_ARRAY +}; + +// Represents a parsed scalar value, it's type, and field offset. +struct Value +{ + Value() : constant("0"), offset(static_cast(~(static_cast(0U)))) {} + Type type; + std::string constant; + voffset_t offset; +}; + +// Helper class that retains the original order of a set of identifiers and +// also provides quick lookup. +template class SymbolTable +{ +public: + ~SymbolTable() + { + for (auto it = vec.begin(); it != vec.end(); ++it) + { + delete *it; + } + } + + bool Add(const std::string &name, T *e) + { + vector_emplace_back(&vec, e); + auto it = dict.find(name); + if (it != dict.end()) + return true; + dict[name] = e; + return false; + } + + void Move(const std::string &oldname, const std::string &newname) + { + auto it = dict.find(oldname); + if (it != dict.end()) + { + auto obj = it->second; + dict.erase(it); + dict[newname] = obj; + } + else + { + FLATBUFFERS_ASSERT(false); + } + } + + T *Lookup(const std::string &name) const + { + auto it = dict.find(name); + return it == dict.end() ? nullptr : it->second; + } + +public: + std::map dict; // quick lookup + std::vector vec; // Used to iterate in order of insertion +}; + +// A name space, as set in the schema. +struct Namespace +{ + Namespace() : from_table(0) {} + + // Given a (potentially unqualified) name, return the "fully qualified" name + // which has a full namespaced descriptor. + // With max_components you can request less than the number of components + // the current namespace has. + std::string GetFullyQualifiedName(const std::string &name, size_t max_components = 1000) const; + + std::vector components; + size_t from_table; // Part of the namespace corresponds to a message/table. +}; + +inline bool operator<(const Namespace &a, const Namespace &b) +{ + size_t min_size = std::min(a.components.size(), b.components.size()); + for (size_t i = 0; i < min_size; ++i) + { + if (a.components[i] != b.components[i]) + return a.components[i] < b.components[i]; + } + return a.components.size() < b.components.size(); +} + +// Base class for all definition types (fields, structs_, enums_). +struct Definition +{ + Definition() + : generated(false), defined_namespace(nullptr), serialized_location(0), index(-1), refcount(1) + { + } + + flatbuffers::Offset>> + SerializeAttributes(FlatBufferBuilder *builder, const Parser &parser) const; + + bool DeserializeAttributes(Parser &parser, const Vector> *attrs); + + std::string name; + std::string file; + std::vector doc_comment; + SymbolTable attributes; + bool generated; // did we already output code for this definition? + Namespace *defined_namespace; // Where it was defined. + + // For use with Serialize() + uoffset_t serialized_location; + int index; // Inside the vector it is stored. + int refcount; +}; + +struct FieldDef : public Definition +{ + FieldDef() + : deprecated(false), key(false), shared(false), native_inline(false), flexbuffer(false), + presence(kDefault), nested_flatbuffer(NULL), padding(0) + { + } + + Offset Serialize(FlatBufferBuilder *builder, uint16_t id, + const Parser &parser) const; + + bool Deserialize(Parser &parser, const reflection::Field *field); + + bool IsScalarOptional() const { return IsScalar(value.type.base_type) && IsOptional(); } + bool IsOptional() const { return presence == kOptional; } + bool IsRequired() const { return presence == kRequired; } + bool IsDefault() const { return presence == kDefault; } + + Value value; + bool deprecated; // Field is allowed to be present in old data, but can't be. + // written in new data nor accessed in new code. + bool key; // Field functions as a key for creating sorted vectors. + bool shared; // Field will be using string pooling (i.e. CreateSharedString) + // as default serialization behavior if field is a string. + bool native_inline; // Field will be defined inline (instead of as a pointer) + // for native tables if field is a struct. + bool flexbuffer; // This field contains FlexBuffer data. + + enum Presence + { + // Field must always be present. + kRequired, + // Non-presence should be signalled to and controlled by users. + kOptional, + // Non-presence is hidden from users. + // Implementations may omit writing default values. + kDefault, + }; + Presence static MakeFieldPresence(bool optional, bool required) + { + FLATBUFFERS_ASSERT(!(required && optional)); + // clang-format off + return required ? FieldDef::kRequired + : optional ? FieldDef::kOptional + : FieldDef::kDefault; + // clang-format on + } + Presence presence; + + StructDef *nested_flatbuffer; // This field contains nested FlatBuffer data. + size_t padding; // Bytes to always pad after this field. +}; + +struct StructDef : public Definition +{ + StructDef() + : fixed(false), predecl(true), sortbysize(true), has_key(false), minalign(1), bytesize(0) + { + } + + void PadLastField(size_t min_align) + { + auto padding = PaddingBytes(bytesize, min_align); + bytesize += padding; + if (fields.vec.size()) + fields.vec.back()->padding = padding; + } + + Offset Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + + bool Deserialize(Parser &parser, const reflection::Object *object); + + SymbolTable fields; + + bool fixed; // If it's struct, not a table. + bool predecl; // If it's used before it was defined. + bool sortbysize; // Whether fields come in the declaration or size order. + bool has_key; // It has a key field. + size_t minalign; // What the whole object needs to be aligned to. + size_t bytesize; // Size if fixed. + + flatbuffers::unique_ptr original_location; +}; + +struct EnumDef; +struct EnumValBuilder; + +struct EnumVal +{ + Offset Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + + bool Deserialize(const Parser &parser, const reflection::EnumVal *val); + + uint64_t GetAsUInt64() const { return static_cast(value); } + int64_t GetAsInt64() const { return value; } + bool IsZero() const { return 0 == value; } + bool IsNonZero() const { return !IsZero(); } + + std::string name; + std::vector doc_comment; + Type union_type; + +private: + friend EnumDef; + friend EnumValBuilder; + friend bool operator==(const EnumVal &lhs, const EnumVal &rhs); + + EnumVal(const std::string &_name, int64_t _val) : name(_name), value(_val) {} + EnumVal() : value(0) {} + + int64_t value; +}; + +struct EnumDef : public Definition +{ + EnumDef() : is_union(false), uses_multiple_type_instances(false) {} + + Offset Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + + bool Deserialize(Parser &parser, const reflection::Enum *values); + + template void ChangeEnumValue(EnumVal *ev, T new_val); + void SortByValue(); + void RemoveDuplicates(); + + std::string AllFlags() const; + const EnumVal *MinValue() const; + const EnumVal *MaxValue() const; + // Returns the number of integer steps from v1 to v2. + uint64_t Distance(const EnumVal *v1, const EnumVal *v2) const; + // Returns the number of integer steps from Min to Max. + uint64_t Distance() const { return Distance(MinValue(), MaxValue()); } + + EnumVal *ReverseLookup(int64_t enum_idx, bool skip_union_default = false) const; + EnumVal *FindByValue(const std::string &constant) const; + + std::string ToString(const EnumVal &ev) const + { + return IsUInt64() ? NumToString(ev.GetAsUInt64()) : NumToString(ev.GetAsInt64()); + } + + size_t size() const { return vals.vec.size(); } + + const std::vector &Vals() const { return vals.vec; } + + const EnumVal *Lookup(const std::string &enum_name) const { return vals.Lookup(enum_name); } + + bool is_union; + // Type is a union which uses type aliases where at least one type is + // available under two different names. + bool uses_multiple_type_instances; + Type underlying_type; + +private: + bool IsUInt64() const { return (BASE_TYPE_ULONG == underlying_type.base_type); } + + friend EnumValBuilder; + SymbolTable vals; +}; + +inline bool IsString(const Type &type) { return type.base_type == BASE_TYPE_STRING; } + +inline bool IsStruct(const Type &type) +{ + return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed; +} + +inline bool IsUnion(const Type &type) +{ + return type.enum_def != nullptr && type.enum_def->is_union; +} + +inline bool IsVector(const Type &type) { return type.base_type == BASE_TYPE_VECTOR; } + +inline bool IsArray(const Type &type) { return type.base_type == BASE_TYPE_ARRAY; } + +inline bool IsSeries(const Type &type) { return IsVector(type) || IsArray(type); } + +inline bool IsEnum(const Type &type) +{ + return type.enum_def != nullptr && IsInteger(type.base_type); +} + +inline size_t InlineSize(const Type &type) +{ + return IsStruct(type) ? type.struct_def->bytesize + : (IsArray(type) ? InlineSize(type.VectorType()) * type.fixed_length + : SizeOf(type.base_type)); +} + +inline size_t InlineAlignment(const Type &type) +{ + if (IsStruct(type)) + { + return type.struct_def->minalign; + } + else if (IsArray(type)) + { + return IsStruct(type.VectorType()) ? type.struct_def->minalign : SizeOf(type.element); + } + else + { + return SizeOf(type.base_type); + } +} +inline bool operator==(const EnumVal &lhs, const EnumVal &rhs) { return lhs.value == rhs.value; } +inline bool operator!=(const EnumVal &lhs, const EnumVal &rhs) { return !(lhs == rhs); } + +inline bool EqualByName(const Type &a, const Type &b) +{ + return a.base_type == b.base_type && a.element == b.element && + (a.struct_def == b.struct_def || a.struct_def->name == b.struct_def->name) && + (a.enum_def == b.enum_def || a.enum_def->name == b.enum_def->name); +} + +struct RPCCall : public Definition +{ + Offset Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + + bool Deserialize(Parser &parser, const reflection::RPCCall *call); + + StructDef *request, *response; +}; + +struct ServiceDef : public Definition +{ + Offset Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::Service *service); + + SymbolTable calls; +}; + +// Container of options that may apply to any of the source/text generators. +struct IDLOptions +{ + bool gen_jvmstatic; + // Use flexbuffers instead for binary and text generation + bool use_flexbuffers; + bool strict_json; + bool output_default_scalars_in_json; + int indent_step; + bool output_enum_identifiers; + bool prefixed_enums; + bool scoped_enums; + bool include_dependence_headers; + bool mutable_buffer; + bool one_file; + bool proto_mode; + bool proto_oneof_union; + bool generate_all; + bool skip_unexpected_fields_in_json; + bool generate_name_strings; + bool generate_object_based_api; + bool gen_compare; + std::string cpp_object_api_pointer_type; + std::string cpp_object_api_string_type; + bool cpp_object_api_string_flexible_constructor; + bool cpp_direct_copy; + bool gen_nullable; + bool java_checkerframework; + bool gen_generated; + std::string object_prefix; + std::string object_suffix; + bool union_value_namespacing; + bool allow_non_utf8; + bool natural_utf8; + std::string include_prefix; + bool keep_include_path; + bool binary_schema_comments; + bool binary_schema_builtins; + bool binary_schema_gen_embed; + std::string go_import; + std::string go_namespace; + bool protobuf_ascii_alike; + bool size_prefixed; + std::string root_type; + bool force_defaults; + bool java_primitive_has_method; + bool cs_gen_json_serializer; + std::vector cpp_includes; + std::string cpp_std; + bool cpp_static_reflection; + std::string proto_namespace_suffix; + std::string filename_suffix; + std::string filename_extension; + bool no_warnings; + + // Possible options for the more general generator below. + enum Language + { + kJava = 1 << 0, + kCSharp = 1 << 1, + kGo = 1 << 2, + kCpp = 1 << 3, + kPython = 1 << 5, + kPhp = 1 << 6, + kJson = 1 << 7, + kBinary = 1 << 8, + kTs = 1 << 9, + kJsonSchema = 1 << 10, + kDart = 1 << 11, + kLua = 1 << 12, + kLobster = 1 << 13, + kRust = 1 << 14, + kKotlin = 1 << 15, + kSwift = 1 << 16, + kMAX + }; + + Language lang; + + enum MiniReflect + { + kNone, + kTypes, + kTypesAndNames + }; + + MiniReflect mini_reflect; + + // If set, require all fields in a table to be explicitly numbered. + bool require_explicit_ids; + + // The corresponding language bit will be set if a language is included + // for code generation. + unsigned long lang_to_generate; + + // If set (default behavior), empty string fields will be set to nullptr to + // make the flatbuffer more compact. + bool set_empty_strings_to_null; + + // If set (default behavior), empty vector fields will be set to nullptr to + // make the flatbuffer more compact. + bool set_empty_vectors_to_null; + + IDLOptions() + : gen_jvmstatic(false), use_flexbuffers(false), strict_json(false), + output_default_scalars_in_json(false), indent_step(2), output_enum_identifiers(true), + prefixed_enums(true), scoped_enums(false), include_dependence_headers(true), + mutable_buffer(false), one_file(false), proto_mode(false), proto_oneof_union(false), + generate_all(false), skip_unexpected_fields_in_json(false), generate_name_strings(false), + generate_object_based_api(false), gen_compare(false), + cpp_object_api_pointer_type("std::unique_ptr"), + cpp_object_api_string_flexible_constructor(false), cpp_direct_copy(true), gen_nullable(false), + java_checkerframework(false), gen_generated(false), object_suffix("T"), + union_value_namespacing(true), allow_non_utf8(false), natural_utf8(false), + keep_include_path(false), binary_schema_comments(false), binary_schema_builtins(false), + binary_schema_gen_embed(false), protobuf_ascii_alike(false), size_prefixed(false), + force_defaults(false), java_primitive_has_method(false), cs_gen_json_serializer(false), + cpp_static_reflection(false), filename_suffix("_generated"), filename_extension(), + no_warnings(false), lang(IDLOptions::kJava), mini_reflect(IDLOptions::kNone), + require_explicit_ids(false), lang_to_generate(0), set_empty_strings_to_null(true), + set_empty_vectors_to_null(true) + { + } +}; + +// This encapsulates where the parser is in the current source file. +struct ParserState +{ + ParserState() + : cursor_(nullptr), line_start_(nullptr), line_(0), token_(-1), + attr_is_trivial_ascii_string_(true) + { + } + +protected: + void ResetState(const char *source) + { + cursor_ = source; + line_ = 0; + MarkNewLine(); + } + + void MarkNewLine() + { + line_start_ = cursor_; + line_ += 1; + } + + int64_t CursorPosition() const + { + FLATBUFFERS_ASSERT(cursor_ && line_start_ && cursor_ >= line_start_); + return static_cast(cursor_ - line_start_); + } + + const char *cursor_; + const char *line_start_; + int line_; // the current line being parsed + int token_; + + // Flag: text in attribute_ is true ASCII string without escape + // sequences. Only printable ASCII (without [\t\r\n]). + // Used for number-in-string (and base64 string in future). + bool attr_is_trivial_ascii_string_; + std::string attribute_; + std::vector doc_comment_; +}; + +// A way to make error propagation less error prone by requiring values to be +// checked. +// Once you create a value of this type you must either: +// - Call Check() on it. +// - Copy or assign it to another value. +// Failure to do so leads to an assert. +// This guarantees that this as return value cannot be ignored. +class CheckedError +{ +public: + explicit CheckedError(bool error) : is_error_(error), has_been_checked_(false) {} + + CheckedError &operator=(const CheckedError &other) + { + is_error_ = other.is_error_; + has_been_checked_ = false; + other.has_been_checked_ = true; + return *this; + } + + CheckedError(const CheckedError &other) + { + *this = other; // Use assignment operator. + } + + ~CheckedError() { FLATBUFFERS_ASSERT(has_been_checked_); } + + bool Check() + { + has_been_checked_ = true; + return is_error_; + } + +private: + bool is_error_; + mutable bool has_been_checked_; +}; + +// Additionally, in GCC we can get these errors statically, for additional +// assurance: +// clang-format off +#ifdef __GNUC__ +#define FLATBUFFERS_CHECKED_ERROR CheckedError \ + __attribute__((warn_unused_result)) +#else +#define FLATBUFFERS_CHECKED_ERROR CheckedError +#endif +// clang-format on + +class Parser : public ParserState +{ +public: + explicit Parser(const IDLOptions &options = IDLOptions()) + : current_namespace_(nullptr), empty_namespace_(nullptr), + flex_builder_(256, flexbuffers::BUILDER_FLAG_SHARE_ALL), root_struct_def_(nullptr), + opts(options), uses_flexbuffers_(false), advanced_features_(0), source_(nullptr), + anonymous_counter_(0), parse_depth_counter_(0) + { + if (opts.force_defaults) + { + builder_.ForceDefaults(true); + } + // Start out with the empty namespace being current. + empty_namespace_ = new Namespace(); + namespaces_.push_back(empty_namespace_); + current_namespace_ = empty_namespace_; + known_attributes_["deprecated"] = true; + known_attributes_["required"] = true; + known_attributes_["key"] = true; + known_attributes_["shared"] = true; + known_attributes_["hash"] = true; + known_attributes_["id"] = true; + known_attributes_["force_align"] = true; + known_attributes_["bit_flags"] = true; + known_attributes_["original_order"] = true; + known_attributes_["nested_flatbuffer"] = true; + known_attributes_["csharp_partial"] = true; + known_attributes_["streaming"] = true; + known_attributes_["idempotent"] = true; + known_attributes_["cpp_type"] = true; + known_attributes_["cpp_ptr_type"] = true; + known_attributes_["cpp_ptr_type_get"] = true; + known_attributes_["cpp_str_type"] = true; + known_attributes_["cpp_str_flex_ctor"] = true; + known_attributes_["native_inline"] = true; + known_attributes_["native_custom_alloc"] = true; + known_attributes_["native_type"] = true; + known_attributes_["native_type_pack_name"] = true; + known_attributes_["native_default"] = true; + known_attributes_["flexbuffer"] = true; + known_attributes_["private"] = true; + } + + ~Parser() + { + for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) + { + delete *it; + } + } + + // Parse the string containing either schema or JSON data, which will + // populate the SymbolTable's or the FlatBufferBuilder above. + // include_paths is used to resolve any include statements, and typically + // should at least include the project path (where you loaded source_ from). + // include_paths must be nullptr terminated if specified. + // If include_paths is nullptr, it will attempt to load from the current + // directory. + // If the source was loaded from a file and isn't an include file, + // supply its name in source_filename. + // All paths specified in this call must be in posix format, if you accept + // paths from user input, please call PosixPath on them first. + bool Parse(const char *_source, const char **include_paths = nullptr, + const char *source_filename = nullptr); + + bool ParseJson(const char *json, const char *json_filename = nullptr); + + // Set the root type. May override the one set in the schema. + bool SetRootType(const char *name); + + // Mark all definitions as already having code generated. + void MarkGenerated(); + + // Get the files recursively included by the given file. The returned + // container will have at least the given file. + std::set GetIncludedFilesRecursive(const std::string &file_name) const; + + // Fills builder_ with a binary version of the schema parsed. + // See reflection/reflection.fbs + void Serialize(); + + // Deserialize a schema buffer + bool Deserialize(const uint8_t *buf, const size_t size); + + // Fills internal structure as if the schema passed had been loaded by parsing + // with Parse except that included filenames will not be populated. + bool Deserialize(const reflection::Schema *schema); + + Type *DeserializeType(const reflection::Type *type); + + // Checks that the schema represented by this parser is a safe evolution + // of the schema provided. Returns non-empty error on any problems. + std::string ConformTo(const Parser &base); + + // Similar to Parse(), but now only accepts JSON to be parsed into a + // FlexBuffer. + bool ParseFlexBuffer(const char *source, const char *source_filename, + flexbuffers::Builder *builder); + + StructDef *LookupStruct(const std::string &id) const; + StructDef *LookupStructThruParentNamespaces(const std::string &id) const; + + std::string UnqualifiedName(const std::string &fullQualifiedName); + + FLATBUFFERS_CHECKED_ERROR Error(const std::string &msg); + + // @brief Verify that any of 'opts.lang_to_generate' supports Optional scalars + // in a schema. + // @param opts Options used to parce a schema and generate code. + static bool SupportsOptionalScalars(const flatbuffers::IDLOptions &opts); + +private: + class ParseDepthGuard; + + void Message(const std::string &msg); + void Warning(const std::string &msg); + FLATBUFFERS_CHECKED_ERROR ParseHexNum(int nibbles, uint64_t *val); + FLATBUFFERS_CHECKED_ERROR Next(); + FLATBUFFERS_CHECKED_ERROR SkipByteOrderMark(); + bool Is(int t) const; + bool IsIdent(const char *id) const; + FLATBUFFERS_CHECKED_ERROR Expect(int t); + std::string TokenToStringId(int t) const; + EnumDef *LookupEnum(const std::string &id); + FLATBUFFERS_CHECKED_ERROR ParseNamespacing(std::string *id, std::string *last); + FLATBUFFERS_CHECKED_ERROR ParseTypeIdent(Type &type); + FLATBUFFERS_CHECKED_ERROR ParseType(Type &type); + FLATBUFFERS_CHECKED_ERROR AddField(StructDef &struct_def, const std::string &name, + const Type &type, FieldDef **dest); + FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def); + FLATBUFFERS_CHECKED_ERROR ParseString(Value &val, bool use_string_pooling); + FLATBUFFERS_CHECKED_ERROR ParseComma(); + FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field, size_t parent_fieldn, + const StructDef *parent_struct_def, uoffset_t count, + bool inside_vector = false); + template + FLATBUFFERS_CHECKED_ERROR ParseTableDelimiters(size_t &fieldn, const StructDef *struct_def, + F body); + FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef &struct_def, std::string *value, + uoffset_t *ovalue); + void SerializeStruct(const StructDef &struct_def, const Value &val); + void SerializeStruct(FlatBufferBuilder &builder, const StructDef &struct_def, const Value &val); + template FLATBUFFERS_CHECKED_ERROR ParseVectorDelimiters(uoffset_t &count, F body); + FLATBUFFERS_CHECKED_ERROR ParseVector(const Type &type, uoffset_t *ovalue, FieldDef *field, + size_t fieldn); + FLATBUFFERS_CHECKED_ERROR ParseArray(Value &array); + FLATBUFFERS_CHECKED_ERROR ParseNestedFlatbuffer(Value &val, FieldDef *field, size_t fieldn, + const StructDef *parent_struct_def); + FLATBUFFERS_CHECKED_ERROR ParseMetaData(SymbolTable *attributes); + FLATBUFFERS_CHECKED_ERROR TryTypedValue(const std::string *name, int dtoken, bool check, Value &e, + BaseType req, bool *destmatch); + FLATBUFFERS_CHECKED_ERROR ParseHash(Value &e, FieldDef *field); + FLATBUFFERS_CHECKED_ERROR TokenError(); + FLATBUFFERS_CHECKED_ERROR ParseSingleValue(const std::string *name, Value &e, bool check_now); + FLATBUFFERS_CHECKED_ERROR ParseFunction(const std::string *name, Value &e); + FLATBUFFERS_CHECKED_ERROR ParseEnumFromString(const Type &type, std::string *result); + StructDef *LookupCreateStruct(const std::string &name, bool create_if_new = true, + bool definition = false); + FLATBUFFERS_CHECKED_ERROR ParseEnum(bool is_union, EnumDef **dest); + FLATBUFFERS_CHECKED_ERROR ParseNamespace(); + FLATBUFFERS_CHECKED_ERROR StartStruct(const std::string &name, StructDef **dest); + FLATBUFFERS_CHECKED_ERROR StartEnum(const std::string &name, bool is_union, EnumDef **dest); + FLATBUFFERS_CHECKED_ERROR ParseDecl(); + FLATBUFFERS_CHECKED_ERROR ParseService(); + FLATBUFFERS_CHECKED_ERROR ParseProtoFields(StructDef *struct_def, bool isextend, + bool inside_oneof); + FLATBUFFERS_CHECKED_ERROR ParseProtoOption(); + FLATBUFFERS_CHECKED_ERROR ParseProtoKey(); + FLATBUFFERS_CHECKED_ERROR ParseProtoDecl(); + FLATBUFFERS_CHECKED_ERROR ParseProtoCurliesOrIdent(); + FLATBUFFERS_CHECKED_ERROR ParseTypeFromProtoType(Type *type); + FLATBUFFERS_CHECKED_ERROR SkipAnyJsonValue(); + FLATBUFFERS_CHECKED_ERROR ParseFlexBufferNumericConstant(flexbuffers::Builder *builder); + FLATBUFFERS_CHECKED_ERROR ParseFlexBufferValue(flexbuffers::Builder *builder); + FLATBUFFERS_CHECKED_ERROR StartParseFile(const char *source, const char *source_filename); + FLATBUFFERS_CHECKED_ERROR ParseRoot(const char *_source, const char **include_paths, + const char *source_filename); + FLATBUFFERS_CHECKED_ERROR DoParse(const char *_source, const char **include_paths, + const char *source_filename, const char *include_filename); + FLATBUFFERS_CHECKED_ERROR DoParseJson(); + FLATBUFFERS_CHECKED_ERROR CheckClash(std::vector &fields, StructDef *struct_def, + const char *suffix, BaseType baseType); + FLATBUFFERS_CHECKED_ERROR ParseAlignAttribute(const std::string &align_constant, size_t min_align, + size_t *align); + + bool SupportsAdvancedUnionFeatures() const; + bool SupportsAdvancedArrayFeatures() const; + bool SupportsOptionalScalars() const; + bool SupportsDefaultVectorsAndStrings() const; + Namespace *UniqueNamespace(Namespace *ns); + + FLATBUFFERS_CHECKED_ERROR RecurseError(); + template CheckedError Recurse(F f); + +public: + SymbolTable types_; + SymbolTable structs_; + SymbolTable enums_; + SymbolTable services_; + std::vector namespaces_; + Namespace *current_namespace_; + Namespace *empty_namespace_; + std::string error_; // User readable error_ if Parse() == false + + FlatBufferBuilder builder_; // any data contained in the file + flexbuffers::Builder flex_builder_; + flexbuffers::Reference flex_root_; + StructDef *root_struct_def_; + std::string file_identifier_; + std::string file_extension_; + + std::map included_files_; + std::map> files_included_per_file_; + std::vector native_included_files_; + + std::map known_attributes_; + + IDLOptions opts; + bool uses_flexbuffers_; + + uint64_t advanced_features_; + +private: + const char *source_; + + std::string file_being_parsed_; + + std::vector> field_stack_; + + int anonymous_counter_; + int parse_depth_counter_; // stack-overflow guard +}; + +// Utility functions for multiple generators: + +extern std::string MakeCamel(const std::string &in, bool first = true); + +extern std::string MakeScreamingCamel(const std::string &in); + +// Generate text (JSON) from a given FlatBuffer, and a given Parser +// object that has been populated with the corresponding schema. +// If ident_step is 0, no indentation will be generated. Additionally, +// if it is less than 0, no linefeeds will be generated either. +// See idl_gen_text.cpp. +// strict_json adds "quotes" around field names if true. +// If the flatbuffer cannot be encoded in JSON (e.g., it contains non-UTF-8 +// byte arrays in String values), returns false. +extern bool GenerateTextFromTable(const Parser &parser, const void *table, + const std::string &tablename, std::string *text); +extern bool GenerateText(const Parser &parser, const void *flatbuffer, std::string *text); +extern bool GenerateTextFile(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Json schema to string +// See idl_gen_json_schema.cpp. +extern bool GenerateJsonSchema(const Parser &parser, std::string *json); + +// Generate binary files from a given FlatBuffer, and a given Parser +// object that has been populated with the corresponding schema. +// See code_generators.cpp. +extern bool GenerateBinary(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a C++ header from the definitions in the Parser object. +// See idl_gen_cpp. +extern bool GenerateCPP(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate C# files from the definitions in the Parser object. +// See idl_gen_csharp.cpp. +extern bool GenerateCSharp(const Parser &parser, const std::string &path, + const std::string &file_name); + +extern bool GenerateDart(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Java files from the definitions in the Parser object. +// See idl_gen_java.cpp. +extern bool GenerateJava(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate JavaScript or TypeScript code from the definitions in the Parser +// object. See idl_gen_js. +extern bool GenerateTS(const Parser &parser, const std::string &path, const std::string &file_name); + +// Generate Go files from the definitions in the Parser object. +// See idl_gen_go.cpp. +extern bool GenerateGo(const Parser &parser, const std::string &path, const std::string &file_name); + +// Generate Php code from the definitions in the Parser object. +// See idl_gen_php. +extern bool GeneratePhp(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Python files from the definitions in the Parser object. +// See idl_gen_python.cpp. +extern bool GeneratePython(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Lobster files from the definitions in the Parser object. +// See idl_gen_lobster.cpp. +extern bool GenerateLobster(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Lua files from the definitions in the Parser object. +// See idl_gen_lua.cpp. +extern bool GenerateLua(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Rust files from the definitions in the Parser object. +// See idl_gen_rust.cpp. +extern bool GenerateRust(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Json schema file +// See idl_gen_json_schema.cpp. +extern bool GenerateJsonSchema(const Parser &parser, const std::string &path, + const std::string &file_name); + +extern bool GenerateKotlin(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate Swift classes. +// See idl_gen_swift.cpp +extern bool GenerateSwift(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a schema file from the internal representation, useful after +// parsing a .proto schema. +extern std::string GenerateFBS(const Parser &parser, const std::string &file_name); +extern bool GenerateFBS(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for the generated TypeScript code. +// See idl_gen_ts.cpp. +extern std::string TSMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for the generated C++ header. +// See idl_gen_cpp.cpp. +extern std::string CPPMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for the generated Dart code +// see idl_gen_dart.cpp +extern std::string DartMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for the generated Rust code. +// See idl_gen_rust.cpp. +extern std::string RustMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for generated Java or C# files. +// See code_generators.cpp. +extern std::string JavaCSharpMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate a make rule for the generated text (JSON) files. +// See idl_gen_text.cpp. +extern std::string TextMakeRule(const Parser &parser, const std::string &path, + const std::string &file_names); + +// Generate a make rule for the generated binary files. +// See code_generators.cpp. +extern std::string BinaryMakeRule(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate GRPC Cpp interfaces. +// See idl_gen_grpc.cpp. +bool GenerateCppGRPC(const Parser &parser, const std::string &path, const std::string &file_name); + +// Generate GRPC Go interfaces. +// See idl_gen_grpc.cpp. +bool GenerateGoGRPC(const Parser &parser, const std::string &path, const std::string &file_name); + +// Generate GRPC Java classes. +// See idl_gen_grpc.cpp +bool GenerateJavaGRPC(const Parser &parser, const std::string &path, const std::string &file_name); + +// Generate GRPC Python interfaces. +// See idl_gen_grpc.cpp. +bool GeneratePythonGRPC(const Parser &parser, const std::string &path, + const std::string &file_name); + +// Generate GRPC Swift interfaces. +// See idl_gen_grpc.cpp. +extern bool GenerateSwiftGRPC(const Parser &parser, const std::string &path, + const std::string &file_name); + +extern bool GenerateTSGRPC(const Parser &parser, const std::string &path, + const std::string &file_name); +} // namespace flatbuffers + +#endif // FLATBUFFERS_IDL_H_ diff --git a/onert-micro/externals/flatbuffers/minireflect.h b/onert-micro/externals/flatbuffers/minireflect.h new file mode 100644 index 0000000..8b733a4 --- /dev/null +++ b/onert-micro/externals/flatbuffers/minireflect.h @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_MINIREFLECT_H_ +#define FLATBUFFERS_MINIREFLECT_H_ + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/util.h" + +namespace flatbuffers +{ + +// Utilities that can be used with the "mini reflection" tables present +// in generated code with --reflect-types (only types) or --reflect-names +// (also names). +// This allows basic reflection functionality such as pretty-printing +// that does not require the use of the schema parser or loading of binary +// schema files at runtime (reflection.h). + +// For any of the functions below that take `const TypeTable *`, you pass +// `FooTypeTable()` if the type of the root is `Foo`. + +// First, a generic iterator that can be used by multiple algorithms. + +struct IterationVisitor +{ + // These mark the scope of a table or struct. + virtual void StartSequence() {} + virtual void EndSequence() {} + // Called for each field regardless of whether it is present or not. + // If not present, val == nullptr. set_idx is the index of all set fields. + virtual void Field(size_t /*field_idx*/, size_t /*set_idx*/, ElementaryType /*type*/, + bool /*is_vector*/, const TypeTable * /*type_table*/, const char * /*name*/, + const uint8_t * /*val*/) + { + } + // Called for a value that is actually present, after a field, or as part + // of a vector. + virtual void UType(uint8_t, const char *) {} + virtual void Bool(bool) {} + virtual void Char(int8_t, const char *) {} + virtual void UChar(uint8_t, const char *) {} + virtual void Short(int16_t, const char *) {} + virtual void UShort(uint16_t, const char *) {} + virtual void Int(int32_t, const char *) {} + virtual void UInt(uint32_t, const char *) {} + virtual void Long(int64_t) {} + virtual void ULong(uint64_t) {} + virtual void Float(float) {} + virtual void Double(double) {} + virtual void String(const String *) {} + virtual void Unknown(const uint8_t *) {} // From a future version. + // These mark the scope of a vector. + virtual void StartVector() {} + virtual void EndVector() {} + virtual void Element(size_t /*i*/, ElementaryType /*type*/, const TypeTable * /*type_table*/, + const uint8_t * /*val*/) + { + } + virtual ~IterationVisitor() {} +}; + +inline size_t InlineSize(ElementaryType type, const TypeTable *type_table) +{ + switch (type) + { + case ET_UTYPE: + case ET_BOOL: + case ET_CHAR: + case ET_UCHAR: + return 1; + case ET_SHORT: + case ET_USHORT: + return 2; + case ET_INT: + case ET_UINT: + case ET_FLOAT: + case ET_STRING: + return 4; + case ET_LONG: + case ET_ULONG: + case ET_DOUBLE: + return 8; + case ET_SEQUENCE: + switch (type_table->st) + { + case ST_TABLE: + case ST_UNION: + return 4; + case ST_STRUCT: + return static_cast(type_table->values[type_table->num_elems]); + default: + FLATBUFFERS_ASSERT(false); + return 1; + } + default: + FLATBUFFERS_ASSERT(false); + return 1; + } +} + +inline int64_t LookupEnum(int64_t enum_val, const int64_t *values, size_t num_values) +{ + if (!values) + return enum_val; + for (size_t i = 0; i < num_values; i++) + { + if (enum_val == values[i]) + return static_cast(i); + } + return -1; // Unknown enum value. +} + +template const char *EnumName(T tval, const TypeTable *type_table) +{ + if (!type_table || !type_table->names) + return nullptr; + auto i = LookupEnum(static_cast(tval), type_table->values, type_table->num_elems); + if (i >= 0 && i < static_cast(type_table->num_elems)) + { + return type_table->names[i]; + } + return nullptr; +} + +void IterateObject(const uint8_t *obj, const TypeTable *type_table, IterationVisitor *visitor); + +inline void IterateValue(ElementaryType type, const uint8_t *val, const TypeTable *type_table, + const uint8_t *prev_val, soffset_t vector_index, IterationVisitor *visitor) +{ + switch (type) + { + case ET_UTYPE: + { + auto tval = ReadScalar(val); + visitor->UType(tval, EnumName(tval, type_table)); + break; + } + case ET_BOOL: + { + visitor->Bool(ReadScalar(val) != 0); + break; + } + case ET_CHAR: + { + auto tval = ReadScalar(val); + visitor->Char(tval, EnumName(tval, type_table)); + break; + } + case ET_UCHAR: + { + auto tval = ReadScalar(val); + visitor->UChar(tval, EnumName(tval, type_table)); + break; + } + case ET_SHORT: + { + auto tval = ReadScalar(val); + visitor->Short(tval, EnumName(tval, type_table)); + break; + } + case ET_USHORT: + { + auto tval = ReadScalar(val); + visitor->UShort(tval, EnumName(tval, type_table)); + break; + } + case ET_INT: + { + auto tval = ReadScalar(val); + visitor->Int(tval, EnumName(tval, type_table)); + break; + } + case ET_UINT: + { + auto tval = ReadScalar(val); + visitor->UInt(tval, EnumName(tval, type_table)); + break; + } + case ET_LONG: + { + visitor->Long(ReadScalar(val)); + break; + } + case ET_ULONG: + { + visitor->ULong(ReadScalar(val)); + break; + } + case ET_FLOAT: + { + visitor->Float(ReadScalar(val)); + break; + } + case ET_DOUBLE: + { + visitor->Double(ReadScalar(val)); + break; + } + case ET_STRING: + { + val += ReadScalar(val); + visitor->String(reinterpret_cast(val)); + break; + } + case ET_SEQUENCE: + { + switch (type_table->st) + { + case ST_TABLE: + val += ReadScalar(val); + IterateObject(val, type_table, visitor); + break; + case ST_STRUCT: + IterateObject(val, type_table, visitor); + break; + case ST_UNION: + { + val += ReadScalar(val); + FLATBUFFERS_ASSERT(prev_val); + auto union_type = *prev_val; // Always a uint8_t. + if (vector_index >= 0) + { + auto type_vec = reinterpret_cast *>(prev_val); + union_type = type_vec->Get(static_cast(vector_index)); + } + auto type_code_idx = LookupEnum(union_type, type_table->values, type_table->num_elems); + if (type_code_idx >= 0 && type_code_idx < static_cast(type_table->num_elems)) + { + auto type_code = type_table->type_codes[type_code_idx]; + switch (type_code.base_type) + { + case ET_SEQUENCE: + { + auto ref = type_table->type_refs[type_code.sequence_ref](); + IterateObject(val, ref, visitor); + break; + } + case ET_STRING: + visitor->String(reinterpret_cast(val)); + break; + default: + visitor->Unknown(val); + } + } + else + { + visitor->Unknown(val); + } + break; + } + case ST_ENUM: + FLATBUFFERS_ASSERT(false); + break; + } + break; + } + default: + { + visitor->Unknown(val); + break; + } + } +} + +inline void IterateObject(const uint8_t *obj, const TypeTable *type_table, + IterationVisitor *visitor) +{ + visitor->StartSequence(); + const uint8_t *prev_val = nullptr; + size_t set_idx = 0; + size_t array_idx = 0; + for (size_t i = 0; i < type_table->num_elems; i++) + { + auto type_code = type_table->type_codes[i]; + auto type = static_cast(type_code.base_type); + auto is_repeating = type_code.is_repeating != 0; + auto ref_idx = type_code.sequence_ref; + const TypeTable *ref = nullptr; + if (ref_idx >= 0) + { + ref = type_table->type_refs[ref_idx](); + } + auto name = type_table->names ? type_table->names[i] : nullptr; + const uint8_t *val = nullptr; + if (type_table->st == ST_TABLE) + { + val = reinterpret_cast(obj)->GetAddressOf( + FieldIndexToOffset(static_cast(i))); + } + else + { + val = obj + type_table->values[i]; + } + visitor->Field(i, set_idx, type, is_repeating, ref, name, val); + if (val) + { + set_idx++; + if (is_repeating) + { + auto elem_ptr = val; + size_t size = 0; + if (type_table->st == ST_TABLE) + { + // variable length vector + val += ReadScalar(val); + auto vec = reinterpret_cast *>(val); + elem_ptr = vec->Data(); + size = vec->size(); + } + else + { + // otherwise fixed size array + size = type_table->array_sizes[array_idx]; + ++array_idx; + } + visitor->StartVector(); + for (size_t j = 0; j < size; j++) + { + visitor->Element(j, type, ref, elem_ptr); + IterateValue(type, elem_ptr, ref, prev_val, static_cast(j), visitor); + elem_ptr += InlineSize(type, ref); + } + visitor->EndVector(); + } + else + { + IterateValue(type, val, ref, prev_val, -1, visitor); + } + } + prev_val = val; + } + visitor->EndSequence(); +} + +inline void IterateFlatBuffer(const uint8_t *buffer, const TypeTable *type_table, + IterationVisitor *callback) +{ + IterateObject(GetRoot(buffer), type_table, callback); +} + +// Outputting a Flatbuffer to a string. Tries to conform as close to JSON / +// the output generated by idl_gen_text.cpp. + +struct ToStringVisitor : public IterationVisitor +{ + std::string s; + std::string d; + bool q; + std::string in; + size_t indent_level; + bool vector_delimited; + ToStringVisitor(std::string delimiter, bool quotes, std::string indent, bool vdelimited = true) + : d(delimiter), q(quotes), in(indent), indent_level(0), vector_delimited(vdelimited) + { + } + ToStringVisitor(std::string delimiter) + : d(delimiter), q(false), in(""), indent_level(0), vector_delimited(true) + { + } + + void append_indent() + { + for (size_t i = 0; i < indent_level; i++) + { + s += in; + } + } + + void StartSequence() + { + s += "{"; + s += d; + indent_level++; + } + void EndSequence() + { + s += d; + indent_level--; + append_indent(); + s += "}"; + } + void Field(size_t /*field_idx*/, size_t set_idx, ElementaryType /*type*/, bool /*is_vector*/, + const TypeTable * /*type_table*/, const char *name, const uint8_t *val) + { + if (!val) + return; + if (set_idx) + { + s += ","; + s += d; + } + append_indent(); + if (name) + { + if (q) + s += "\""; + s += name; + if (q) + s += "\""; + s += ": "; + } + } + template void Named(T x, const char *name) + { + if (name) + { + if (q) + s += "\""; + s += name; + if (q) + s += "\""; + } + else + { + s += NumToString(x); + } + } + void UType(uint8_t x, const char *name) { Named(x, name); } + void Bool(bool x) { s += x ? "true" : "false"; } + void Char(int8_t x, const char *name) { Named(x, name); } + void UChar(uint8_t x, const char *name) { Named(x, name); } + void Short(int16_t x, const char *name) { Named(x, name); } + void UShort(uint16_t x, const char *name) { Named(x, name); } + void Int(int32_t x, const char *name) { Named(x, name); } + void UInt(uint32_t x, const char *name) { Named(x, name); } + void Long(int64_t x) { s += NumToString(x); } + void ULong(uint64_t x) { s += NumToString(x); } + void Float(float x) { s += NumToString(x); } + void Double(double x) { s += NumToString(x); } + void String(const struct String *str) + { + EscapeString(str->c_str(), str->size(), &s, true, false); + } + void Unknown(const uint8_t *) { s += "(?)"; } + void StartVector() + { + s += "["; + if (vector_delimited) + { + s += d; + indent_level++; + append_indent(); + } + else + { + s += " "; + } + } + void EndVector() + { + if (vector_delimited) + { + s += d; + indent_level--; + append_indent(); + } + else + { + s += " "; + } + s += "]"; + } + void Element(size_t i, ElementaryType /*type*/, const TypeTable * /*type_table*/, + const uint8_t * /*val*/) + { + if (i) + { + s += ","; + if (vector_delimited) + { + s += d; + append_indent(); + } + else + { + s += " "; + } + } + } +}; + +inline std::string FlatBufferToString(const uint8_t *buffer, const TypeTable *type_table, + bool multi_line = false, bool vector_delimited = true) +{ + ToStringVisitor tostring_visitor(multi_line ? "\n" : " ", false, "", vector_delimited); + IterateFlatBuffer(buffer, type_table, &tostring_visitor); + return tostring_visitor.s; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_MINIREFLECT_H_ diff --git a/onert-micro/externals/flatbuffers/pch/flatc_pch.h b/onert-micro/externals/flatbuffers/pch/flatc_pch.h new file mode 100644 index 0000000..988fcf3 --- /dev/null +++ b/onert-micro/externals/flatbuffers/pch/flatc_pch.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_FLATC_PCH_H_ +#define FLATBUFFERS_FLATC_PCH_H_ + +// stl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// flatbuffers +#include "flatbuffers/pch/pch.h" +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/flexbuffers.h" +#include "flatbuffers/idl.h" + +#endif // FLATBUFFERS_FLATC_PCH_H_ diff --git a/onert-micro/externals/flatbuffers/pch/pch.h b/onert-micro/externals/flatbuffers/pch/pch.h new file mode 100644 index 0000000..0e7886f --- /dev/null +++ b/onert-micro/externals/flatbuffers/pch/pch.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_PCH_H_ +#define FLATBUFFERS_PCH_H_ + +// stl +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// flatbuffers +#include "flatbuffers/util.h" + +#endif // FLATBUFFERS_PCH_H_ diff --git a/onert-micro/externals/flatbuffers/reflection.h b/onert-micro/externals/flatbuffers/reflection.h new file mode 100644 index 0000000..8e2b155 --- /dev/null +++ b/onert-micro/externals/flatbuffers/reflection.h @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_REFLECTION_H_ +#define FLATBUFFERS_REFLECTION_H_ + +// This is somewhat of a circular dependency because flatc (and thus this +// file) is needed to generate this header in the first place. +// Should normally not be a problem since it can be generated by the +// previous version of flatc whenever this code needs to change. +// See reflection/generate_code.sh +#include "flatbuffers/reflection_generated.h" + +// Helper functionality for reflection. + +namespace flatbuffers +{ + +// ------------------------- GETTERS ------------------------- + +inline bool IsScalar(reflection::BaseType t) +{ + return t >= reflection::UType && t <= reflection::Double; +} +inline bool IsInteger(reflection::BaseType t) +{ + return t >= reflection::UType && t <= reflection::ULong; +} +inline bool IsFloat(reflection::BaseType t) +{ + return t == reflection::Float || t == reflection::Double; +} +inline bool IsLong(reflection::BaseType t) +{ + return t == reflection::Long || t == reflection::ULong; +} + +// Size of a basic type, don't use with structs. +inline size_t GetTypeSize(reflection::BaseType base_type) +{ + // This needs to correspond to the BaseType enum. + static size_t sizes[] = { + 0, // None + 1, // UType + 1, // Bool + 1, // Byte + 1, // UByte + 2, // Short + 2, // UShort + 4, // Int + 4, // UInt + 8, // Long + 8, // ULong + 4, // Float + 8, // Double + 4, // String + 4, // Vector + 4, // Obj + 4, // Union + 0, // Array. Only used in structs. 0 was chosen to prevent out-of-bounds + // errors. + + 0 // MaxBaseType. This must be kept the last entry in this array. + }; + static_assert(sizeof(sizes) / sizeof(size_t) == reflection::MaxBaseType + 1, + "Size of sizes[] array does not match the count of BaseType " + "enum values."); + return sizes[base_type]; +} + +// Same as above, but now correctly returns the size of a struct if +// the field (or vector element) is a struct. +inline size_t GetTypeSizeInline(reflection::BaseType base_type, int type_index, + const reflection::Schema &schema) +{ + if (base_type == reflection::Obj && schema.objects()->Get(type_index)->is_struct()) + { + return schema.objects()->Get(type_index)->bytesize(); + } + else + { + return GetTypeSize(base_type); + } +} + +// Get the root, regardless of what type it is. +inline Table *GetAnyRoot(uint8_t *flatbuf) { return GetMutableRoot
(flatbuf); } +inline const Table *GetAnyRoot(const uint8_t *flatbuf) { return GetRoot
(flatbuf); } + +// Get a field's default, if you know it's an integer, and its exact type. +template T GetFieldDefaultI(const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type())); + return static_cast(field.default_integer()); +} + +// Get a field's default, if you know it's floating point and its exact type. +template T GetFieldDefaultF(const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type())); + return static_cast(field.default_real()); +} + +// Get a field, if you know it's an integer, and its exact type. +template T GetFieldI(const Table &table, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type())); + return table.GetField(field.offset(), static_cast(field.default_integer())); +} + +// Get a field, if you know it's floating point and its exact type. +template T GetFieldF(const Table &table, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(field.type()->base_type())); + return table.GetField(field.offset(), static_cast(field.default_real())); +} + +// Get a field, if you know it's a string. +inline const String *GetFieldS(const Table &table, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::String); + return table.GetPointer(field.offset()); +} + +// Get a field, if you know it's a vector. +template Vector *GetFieldV(const Table &table, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Vector && + sizeof(T) == GetTypeSize(field.type()->element())); + return table.GetPointer *>(field.offset()); +} + +// Get a field, if you know it's a vector, generically. +// To actually access elements, use the return value together with +// field.type()->element() in any of GetAnyVectorElemI below etc. +inline VectorOfAny *GetFieldAnyV(const Table &table, const reflection::Field &field) +{ + return table.GetPointer(field.offset()); +} + +// Get a field, if you know it's a table. +inline Table *GetFieldT(const Table &table, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj || + field.type()->base_type() == reflection::Union); + return table.GetPointer
(field.offset()); +} + +// Get a field, if you know it's a struct. +inline const Struct *GetFieldStruct(const Table &table, const reflection::Field &field) +{ + // TODO: This does NOT check if the field is a table or struct, but we'd need + // access to the schema to check the is_struct flag. + FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj); + return table.GetStruct(field.offset()); +} + +// Get a structure's field, if you know it's a struct. +inline const Struct *GetFieldStruct(const Struct &structure, const reflection::Field &field) +{ + FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj); + return structure.GetStruct(field.offset()); +} + +// Raw helper functions used below: get any value in memory as a 64bit int, a +// double or a string. +// All scalars get static_cast to an int64_t, strings use strtoull, every other +// data type returns 0. +int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data); +// All scalars static cast to double, strings use strtod, every other data +// type is 0.0. +double GetAnyValueF(reflection::BaseType type, const uint8_t *data); +// All scalars converted using stringstream, strings as-is, and all other +// data types provide some level of debug-pretty-printing. +std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, + const reflection::Schema *schema, int type_index); + +// Get any table field as a 64bit int, regardless of what type it is. +inline int64_t GetAnyFieldI(const Table &table, const reflection::Field &field) +{ + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr) : field.default_integer(); +} + +// Get any table field as a double, regardless of what type it is. +inline double GetAnyFieldF(const Table &table, const reflection::Field &field) +{ + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr) : field.default_real(); +} + +// Get any table field as a string, regardless of what type it is. +// You may pass nullptr for the schema if you don't care to have fields that +// are of table type pretty-printed. +inline std::string GetAnyFieldS(const Table &table, const reflection::Field &field, + const reflection::Schema *schema) +{ + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr + ? GetAnyValueS(field.type()->base_type(), field_ptr, schema, field.type()->index()) + : ""; +} + +// Get any struct field as a 64bit int, regardless of what type it is. +inline int64_t GetAnyFieldI(const Struct &st, const reflection::Field &field) +{ + return GetAnyValueI(field.type()->base_type(), st.GetAddressOf(field.offset())); +} + +// Get any struct field as a double, regardless of what type it is. +inline double GetAnyFieldF(const Struct &st, const reflection::Field &field) +{ + return GetAnyValueF(field.type()->base_type(), st.GetAddressOf(field.offset())); +} + +// Get any struct field as a string, regardless of what type it is. +inline std::string GetAnyFieldS(const Struct &st, const reflection::Field &field) +{ + return GetAnyValueS(field.type()->base_type(), st.GetAddressOf(field.offset()), nullptr, -1); +} + +// Get any vector element as a 64bit int, regardless of what type it is. +inline int64_t GetAnyVectorElemI(const VectorOfAny *vec, reflection::BaseType elem_type, size_t i) +{ + return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i); +} + +// Get any vector element as a double, regardless of what type it is. +inline double GetAnyVectorElemF(const VectorOfAny *vec, reflection::BaseType elem_type, size_t i) +{ + return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i); +} + +// Get any vector element as a string, regardless of what type it is. +inline std::string GetAnyVectorElemS(const VectorOfAny *vec, reflection::BaseType elem_type, + size_t i) +{ + return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, nullptr, -1); +} + +// Get a vector element that's a table/string/vector from a generic vector. +// Pass Table/String/VectorOfAny as template parameter. +// Warning: does no typechecking. +template T *GetAnyVectorElemPointer(const VectorOfAny *vec, size_t i) +{ + auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i; + return reinterpret_cast(elem_ptr + ReadScalar(elem_ptr)); +} + +// Get the inline-address of a vector element. Useful for Structs (pass Struct +// as template arg), or being able to address a range of scalars in-line. +// Get elem_size from GetTypeSizeInline(). +// Note: little-endian data on all platforms, use EndianScalar() instead of +// raw pointer access with scalars). +template +T *GetAnyVectorElemAddressOf(const VectorOfAny *vec, size_t i, size_t elem_size) +{ + return reinterpret_cast(vec->Data() + elem_size * i); +} + +// Similarly, for elements of tables. +template T *GetAnyFieldAddressOf(const Table &table, const reflection::Field &field) +{ + return reinterpret_cast(table.GetAddressOf(field.offset())); +} + +// Similarly, for elements of structs. +template T *GetAnyFieldAddressOf(const Struct &st, const reflection::Field &field) +{ + return reinterpret_cast(st.GetAddressOf(field.offset())); +} + +// ------------------------- SETTERS ------------------------- + +// Set any scalar field, if you know its exact type. +template bool SetField(Table *table, const reflection::Field &field, T val) +{ + reflection::BaseType type = field.type()->base_type(); + if (!IsScalar(type)) + { + return false; + } + FLATBUFFERS_ASSERT(sizeof(T) == GetTypeSize(type)); + T def; + if (IsInteger(type)) + { + def = GetFieldDefaultI(field); + } + else + { + FLATBUFFERS_ASSERT(IsFloat(type)); + def = GetFieldDefaultF(field); + } + return table->SetField(field.offset(), val, def); +} + +// Raw helper functions used below: set any value in memory as a 64bit int, a +// double or a string. +// These work for all scalar values, but do nothing for other data types. +// To set a string, see SetString below. +void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val); +void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val); +void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val); + +// Set any table field as a 64bit int, regardless of type what it is. +inline bool SetAnyFieldI(Table *table, const reflection::Field &field, int64_t val) +{ + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) + return val == GetFieldDefaultI(field); + SetAnyValueI(field.type()->base_type(), field_ptr, val); + return true; +} + +// Set any table field as a double, regardless of what type it is. +inline bool SetAnyFieldF(Table *table, const reflection::Field &field, double val) +{ + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) + return val == GetFieldDefaultF(field); + SetAnyValueF(field.type()->base_type(), field_ptr, val); + return true; +} + +// Set any table field as a string, regardless of what type it is. +inline bool SetAnyFieldS(Table *table, const reflection::Field &field, const char *val) +{ + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) + return false; + SetAnyValueS(field.type()->base_type(), field_ptr, val); + return true; +} + +// Set any struct field as a 64bit int, regardless of type what it is. +inline void SetAnyFieldI(Struct *st, const reflection::Field &field, int64_t val) +{ + SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()), val); +} + +// Set any struct field as a double, regardless of type what it is. +inline void SetAnyFieldF(Struct *st, const reflection::Field &field, double val) +{ + SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()), val); +} + +// Set any struct field as a string, regardless of type what it is. +inline void SetAnyFieldS(Struct *st, const reflection::Field &field, const char *val) +{ + SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()), val); +} + +// Set any vector element as a 64bit int, regardless of type what it is. +inline void SetAnyVectorElemI(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, + int64_t val) +{ + SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + +// Set any vector element as a double, regardless of type what it is. +inline void SetAnyVectorElemF(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, + double val) +{ + SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + +// Set any vector element as a string, regardless of type what it is. +inline void SetAnyVectorElemS(VectorOfAny *vec, reflection::BaseType elem_type, size_t i, + const char *val) +{ + SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + +// ------------------------- RESIZING SETTERS ------------------------- + +// "smart" pointer for use with resizing vectors: turns a pointer inside +// a vector into a relative offset, such that it is not affected by resizes. +template class pointer_inside_vector +{ +public: + pointer_inside_vector(T *ptr, std::vector &vec) + : offset_(reinterpret_cast(ptr) - + reinterpret_cast(flatbuffers::vector_data(vec))), + vec_(vec) + { + } + + T *operator*() const + { + return reinterpret_cast(reinterpret_cast(flatbuffers::vector_data(vec_)) + + offset_); + } + T *operator->() const { return operator*(); } + +private: + size_t offset_; + std::vector &vec_; +}; + +// Helper to create the above easily without specifying template args. +template pointer_inside_vector piv(T *ptr, std::vector &vec) +{ + return pointer_inside_vector(ptr, vec); +} + +inline const char *UnionTypeFieldSuffix() { return "_type"; } + +// Helper to figure out the actual table type a union refers to. +inline const reflection::Object &GetUnionType(const reflection::Schema &schema, + const reflection::Object &parent, + const reflection::Field &unionfield, + const Table &table) +{ + auto enumdef = schema.enums()->Get(unionfield.type()->index()); + // TODO: this is clumsy and slow, but no other way to find it? + auto type_field = + parent.fields()->LookupByKey((unionfield.name()->str() + UnionTypeFieldSuffix()).c_str()); + FLATBUFFERS_ASSERT(type_field); + auto union_type = GetFieldI(table, *type_field); + auto enumval = enumdef->values()->LookupByKey(union_type); + return *enumval->object(); +} + +// Changes the contents of a string inside a FlatBuffer. FlatBuffer must +// live inside a std::vector so we can resize the buffer if needed. +// "str" must live inside "flatbuf" and may be invalidated after this call. +// If your FlatBuffer's root table is not the schema's root table, you should +// pass in your root_table type as well. +void SetString(const reflection::Schema &schema, const std::string &val, const String *str, + std::vector *flatbuf, const reflection::Object *root_table = nullptr); + +// Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must +// live inside a std::vector so we can resize the buffer if needed. +// "vec" must live inside "flatbuf" and may be invalidated after this call. +// If your FlatBuffer's root table is not the schema's root table, you should +// pass in your root_table type as well. +uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, + const VectorOfAny *vec, uoffset_t num_elems, uoffset_t elem_size, + std::vector *flatbuf, + const reflection::Object *root_table = nullptr); + +template +void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, const Vector *vec, + std::vector *flatbuf, const reflection::Object *root_table = nullptr) +{ + auto delta_elem = static_cast(newsize) - static_cast(vec->size()); + auto newelems = + ResizeAnyVector(schema, newsize, reinterpret_cast(vec), vec->size(), + static_cast(sizeof(T)), flatbuf, root_table); + // Set new elements to "val". + for (int i = 0; i < delta_elem; i++) + { + auto loc = newelems + i * sizeof(T); + auto is_scalar = flatbuffers::is_scalar::value; + if (is_scalar) + { + WriteScalar(loc, val); + } + else + { // struct + *reinterpret_cast(loc) = val; + } + } +} + +// Adds any new data (in the form of a new FlatBuffer) to an existing +// FlatBuffer. This can be used when any of the above methods are not +// sufficient, in particular for adding new tables and new fields. +// This is potentially slightly less efficient than a FlatBuffer constructed +// in one piece, since the new FlatBuffer doesn't share any vtables with the +// existing one. +// The return value can now be set using Vector::MutateOffset or SetFieldT +// below. +const uint8_t *AddFlatBuffer(std::vector &flatbuf, const uint8_t *newbuf, size_t newlen); + +inline bool SetFieldT(Table *table, const reflection::Field &field, const uint8_t *val) +{ + FLATBUFFERS_ASSERT(sizeof(uoffset_t) == GetTypeSize(field.type()->base_type())); + return table->SetPointer(field.offset(), val); +} + +// ------------------------- COPYING ------------------------- + +// Generic copying of tables from a FlatBuffer into a FlatBuffer builder. +// Can be used to do any kind of merging/selecting you may want to do out +// of existing buffers. Also useful to reconstruct a whole buffer if the +// above resizing functionality has introduced garbage in a buffer you want +// to remove. +// Note: this does not deal with DAGs correctly. If the table passed forms a +// DAG, the copy will be a tree instead (with duplicates). Strings can be +// shared however, by passing true for use_string_pooling. + +Offset CopyTable(FlatBufferBuilder &fbb, const reflection::Schema &schema, + const reflection::Object &objectdef, const Table &table, + bool use_string_pooling = false); + +// Verifies the provided flatbuffer using reflection. +// root should point to the root type for this flatbuffer. +// buf should point to the start of flatbuffer data. +// length specifies the size of the flatbuffer data. +bool Verify(const reflection::Schema &schema, const reflection::Object &root, const uint8_t *buf, + size_t length, uoffset_t max_depth = 64, uoffset_t max_tables = 1000000); + +} // namespace flatbuffers + +#endif // FLATBUFFERS_REFLECTION_H_ diff --git a/onert-micro/externals/flatbuffers/reflection_generated.h b/onert-micro/externals/flatbuffers/reflection_generated.h new file mode 100644 index 0000000..9c57dd1 --- /dev/null +++ b/onert-micro/externals/flatbuffers/reflection_generated.h @@ -0,0 +1,1257 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// automatically generated by the FlatBuffers compiler, do not modify + +#ifndef FLATBUFFERS_GENERATED_REFLECTION_REFLECTION_H_ +#define FLATBUFFERS_GENERATED_REFLECTION_REFLECTION_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace reflection +{ + +struct Type; +struct TypeBuilder; + +struct KeyValue; +struct KeyValueBuilder; + +struct EnumVal; +struct EnumValBuilder; + +struct Enum; +struct EnumBuilder; + +struct Field; +struct FieldBuilder; + +struct Object; +struct ObjectBuilder; + +struct RPCCall; +struct RPCCallBuilder; + +struct Service; +struct ServiceBuilder; + +struct Schema; +struct SchemaBuilder; + +enum BaseType +{ + None = 0, + UType = 1, + Bool = 2, + Byte = 3, + UByte = 4, + Short = 5, + UShort = 6, + Int = 7, + UInt = 8, + Long = 9, + ULong = 10, + Float = 11, + Double = 12, + String = 13, + Vector = 14, + Obj = 15, + Union = 16, + Array = 17, + MaxBaseType = 18 +}; + +inline const BaseType (&EnumValuesBaseType())[19] +{ + static const BaseType values[] = {None, UType, Bool, Byte, UByte, Short, UShort, + Int, UInt, Long, ULong, Float, Double, String, + Vector, Obj, Union, Array, MaxBaseType}; + return values; +} + +inline const char *const *EnumNamesBaseType() +{ + static const char *const names[20] = {"None", "UType", "Bool", "Byte", "UByte", + "Short", "UShort", "Int", "UInt", "Long", + "ULong", "Float", "Double", "String", "Vector", + "Obj", "Union", "Array", "MaxBaseType", nullptr}; + return names; +} + +inline const char *EnumNameBaseType(BaseType e) +{ + if (flatbuffers::IsOutRange(e, None, MaxBaseType)) + return ""; + const size_t index = static_cast(e); + return EnumNamesBaseType()[index]; +} + +enum AdvancedFeatures +{ + AdvancedArrayFeatures = 1ULL, + AdvancedUnionFeatures = 2ULL, + OptionalScalars = 4ULL, + DefaultVectorsAndStrings = 8ULL +}; + +inline const AdvancedFeatures (&EnumValuesAdvancedFeatures())[4] +{ + static const AdvancedFeatures values[] = {AdvancedArrayFeatures, AdvancedUnionFeatures, + OptionalScalars, DefaultVectorsAndStrings}; + return values; +} + +inline const char *const *EnumNamesAdvancedFeatures() +{ + static const char *const names[9] = {"AdvancedArrayFeatures", + "AdvancedUnionFeatures", + "", + "OptionalScalars", + "", + "", + "", + "DefaultVectorsAndStrings", + nullptr}; + return names; +} + +inline const char *EnumNameAdvancedFeatures(AdvancedFeatures e) +{ + if (flatbuffers::IsOutRange(e, AdvancedArrayFeatures, DefaultVectorsAndStrings)) + return ""; + const size_t index = static_cast(e) - static_cast(AdvancedArrayFeatures); + return EnumNamesAdvancedFeatures()[index]; +} + +struct Type FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TypeBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_BASE_TYPE = 4, + VT_ELEMENT = 6, + VT_INDEX = 8, + VT_FIXED_LENGTH = 10 + }; + reflection::BaseType base_type() const + { + return static_cast(GetField(VT_BASE_TYPE, 0)); + } + reflection::BaseType element() const + { + return static_cast(GetField(VT_ELEMENT, 0)); + } + int32_t index() const { return GetField(VT_INDEX, -1); } + uint16_t fixed_length() const { return GetField(VT_FIXED_LENGTH, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_BASE_TYPE) && + VerifyField(verifier, VT_ELEMENT) && VerifyField(verifier, VT_INDEX) && + VerifyField(verifier, VT_FIXED_LENGTH) && verifier.EndTable(); + } +}; + +struct TypeBuilder +{ + typedef Type Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_base_type(reflection::BaseType base_type) + { + fbb_.AddElement(Type::VT_BASE_TYPE, static_cast(base_type), 0); + } + void add_element(reflection::BaseType element) + { + fbb_.AddElement(Type::VT_ELEMENT, static_cast(element), 0); + } + void add_index(int32_t index) { fbb_.AddElement(Type::VT_INDEX, index, -1); } + void add_fixed_length(uint16_t fixed_length) + { + fbb_.AddElement(Type::VT_FIXED_LENGTH, fixed_length, 0); + } + explicit TypeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateType(flatbuffers::FlatBufferBuilder &_fbb, + reflection::BaseType base_type = reflection::None, + reflection::BaseType element = reflection::None, + int32_t index = -1, uint16_t fixed_length = 0) +{ + TypeBuilder builder_(_fbb); + builder_.add_index(index); + builder_.add_fixed_length(fixed_length); + builder_.add_element(element); + builder_.add_base_type(base_type); + return builder_.Finish(); +} + +struct KeyValue FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef KeyValueBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_KEY = 4, + VT_VALUE = 6 + }; + const flatbuffers::String *key() const { return GetPointer(VT_KEY); } + bool KeyCompareLessThan(const KeyValue *o) const { return *key() < *o->key(); } + int KeyCompareWithValue(const char *val) const { return strcmp(key()->c_str(), val); } + const flatbuffers::String *value() const + { + return GetPointer(VT_VALUE); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_KEY) && + verifier.VerifyString(key()) && VerifyOffset(verifier, VT_VALUE) && + verifier.VerifyString(value()) && verifier.EndTable(); + } +}; + +struct KeyValueBuilder +{ + typedef KeyValue Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_key(flatbuffers::Offset key) + { + fbb_.AddOffset(KeyValue::VT_KEY, key); + } + void add_value(flatbuffers::Offset value) + { + fbb_.AddOffset(KeyValue::VT_VALUE, value); + } + explicit KeyValueBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, KeyValue::VT_KEY); + return o; + } +}; + +inline flatbuffers::Offset +CreateKeyValue(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset key = 0, + flatbuffers::Offset value = 0) +{ + KeyValueBuilder builder_(_fbb); + builder_.add_value(value); + builder_.add_key(key); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateKeyValueDirect(flatbuffers::FlatBufferBuilder &_fbb, + const char *key = nullptr, + const char *value = nullptr) +{ + auto key__ = key ? _fbb.CreateString(key) : 0; + auto value__ = value ? _fbb.CreateString(value) : 0; + return reflection::CreateKeyValue(_fbb, key__, value__); +} + +struct EnumVal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef EnumValBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_VALUE = 6, + VT_OBJECT = 8, + VT_UNION_TYPE = 10, + VT_DOCUMENTATION = 12 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + int64_t value() const { return GetField(VT_VALUE, 0); } + bool KeyCompareLessThan(const EnumVal *o) const { return value() < o->value(); } + int KeyCompareWithValue(int64_t val) const + { + return static_cast(value() > val) - static_cast(value() < val); + } + const reflection::Object *object() const + { + return GetPointer(VT_OBJECT); + } + const reflection::Type *union_type() const + { + return GetPointer(VT_UNION_TYPE); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyField(verifier, VT_VALUE) && + VerifyOffset(verifier, VT_OBJECT) && verifier.VerifyTable(object()) && + VerifyOffset(verifier, VT_UNION_TYPE) && verifier.VerifyTable(union_type()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && verifier.EndTable(); + } +}; + +struct EnumValBuilder +{ + typedef EnumVal Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(EnumVal::VT_NAME, name); + } + void add_value(int64_t value) { fbb_.AddElement(EnumVal::VT_VALUE, value, 0); } + void add_object(flatbuffers::Offset object) + { + fbb_.AddOffset(EnumVal::VT_OBJECT, object); + } + void add_union_type(flatbuffers::Offset union_type) + { + fbb_.AddOffset(EnumVal::VT_UNION_TYPE, union_type); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(EnumVal::VT_DOCUMENTATION, documentation); + } + explicit EnumValBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, EnumVal::VT_NAME); + return o; + } +}; + +inline flatbuffers::Offset CreateEnumVal( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + int64_t value = 0, flatbuffers::Offset object = 0, + flatbuffers::Offset union_type = 0, + flatbuffers::Offset>> documentation = + 0) +{ + EnumValBuilder builder_(_fbb); + builder_.add_value(value); + builder_.add_documentation(documentation); + builder_.add_union_type(union_type); + builder_.add_object(object); + builder_.add_name(name); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateEnumValDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, int64_t value = 0, + flatbuffers::Offset object = 0, + flatbuffers::Offset union_type = 0, + const std::vector> *documentation = nullptr) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateEnumVal(_fbb, name__, value, object, union_type, documentation__); +} + +struct Enum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef EnumBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_VALUES = 6, + VT_IS_UNION = 8, + VT_UNDERLYING_TYPE = 10, + VT_ATTRIBUTES = 12, + VT_DOCUMENTATION = 14 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + bool KeyCompareLessThan(const Enum *o) const { return *name() < *o->name(); } + int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); } + const flatbuffers::Vector> *values() const + { + return GetPointer> *>( + VT_VALUES); + } + bool is_union() const { return GetField(VT_IS_UNION, 0) != 0; } + const reflection::Type *underlying_type() const + { + return GetPointer(VT_UNDERLYING_TYPE); + } + const flatbuffers::Vector> *attributes() const + { + return GetPointer> *>( + VT_ATTRIBUTES); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffsetRequired(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && verifier.VerifyVectorOfTables(values()) && + VerifyField(verifier, VT_IS_UNION) && + VerifyOffsetRequired(verifier, VT_UNDERLYING_TYPE) && + verifier.VerifyTable(underlying_type()) && VerifyOffset(verifier, VT_ATTRIBUTES) && + verifier.VerifyVector(attributes()) && verifier.VerifyVectorOfTables(attributes()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && verifier.EndTable(); + } +}; + +struct EnumBuilder +{ + typedef Enum Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Enum::VT_NAME, name); + } + void add_values( + flatbuffers::Offset>> values) + { + fbb_.AddOffset(Enum::VT_VALUES, values); + } + void add_is_union(bool is_union) + { + fbb_.AddElement(Enum::VT_IS_UNION, static_cast(is_union), 0); + } + void add_underlying_type(flatbuffers::Offset underlying_type) + { + fbb_.AddOffset(Enum::VT_UNDERLYING_TYPE, underlying_type); + } + void add_attributes( + flatbuffers::Offset>> attributes) + { + fbb_.AddOffset(Enum::VT_ATTRIBUTES, attributes); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(Enum::VT_DOCUMENTATION, documentation); + } + explicit EnumBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, Enum::VT_NAME); + fbb_.Required(o, Enum::VT_VALUES); + fbb_.Required(o, Enum::VT_UNDERLYING_TYPE); + return o; + } +}; + +inline flatbuffers::Offset CreateEnum( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + flatbuffers::Offset>> values = 0, + bool is_union = false, flatbuffers::Offset underlying_type = 0, + flatbuffers::Offset>> attributes = + 0, + flatbuffers::Offset>> documentation = + 0) +{ + EnumBuilder builder_(_fbb); + builder_.add_documentation(documentation); + builder_.add_attributes(attributes); + builder_.add_underlying_type(underlying_type); + builder_.add_values(values); + builder_.add_name(name); + builder_.add_is_union(is_union); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateEnumDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, + std::vector> *values = nullptr, bool is_union = false, + flatbuffers::Offset underlying_type = 0, + std::vector> *attributes = nullptr, + const std::vector> *documentation = nullptr) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto values__ = values ? _fbb.CreateVectorOfSortedTables(values) : 0; + auto attributes__ = + attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateEnum(_fbb, name__, values__, is_union, underlying_type, attributes__, + documentation__); +} + +struct Field FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FieldBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_TYPE = 6, + VT_ID = 8, + VT_OFFSET = 10, + VT_DEFAULT_INTEGER = 12, + VT_DEFAULT_REAL = 14, + VT_DEPRECATED = 16, + VT_REQUIRED = 18, + VT_KEY = 20, + VT_ATTRIBUTES = 22, + VT_DOCUMENTATION = 24, + VT_OPTIONAL = 26 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + bool KeyCompareLessThan(const Field *o) const { return *name() < *o->name(); } + int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); } + const reflection::Type *type() const { return GetPointer(VT_TYPE); } + uint16_t id() const { return GetField(VT_ID, 0); } + uint16_t offset() const { return GetField(VT_OFFSET, 0); } + int64_t default_integer() const { return GetField(VT_DEFAULT_INTEGER, 0); } + double default_real() const { return GetField(VT_DEFAULT_REAL, 0.0); } + bool deprecated() const { return GetField(VT_DEPRECATED, 0) != 0; } + bool required() const { return GetField(VT_REQUIRED, 0) != 0; } + bool key() const { return GetField(VT_KEY, 0) != 0; } + const flatbuffers::Vector> *attributes() const + { + return GetPointer> *>( + VT_ATTRIBUTES); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool optional() const { return GetField(VT_OPTIONAL, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffsetRequired(verifier, VT_TYPE) && + verifier.VerifyTable(type()) && VerifyField(verifier, VT_ID) && + VerifyField(verifier, VT_OFFSET) && + VerifyField(verifier, VT_DEFAULT_INTEGER) && + VerifyField(verifier, VT_DEFAULT_REAL) && + VerifyField(verifier, VT_DEPRECATED) && + VerifyField(verifier, VT_REQUIRED) && VerifyField(verifier, VT_KEY) && + VerifyOffset(verifier, VT_ATTRIBUTES) && verifier.VerifyVector(attributes()) && + verifier.VerifyVectorOfTables(attributes()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && + VerifyField(verifier, VT_OPTIONAL) && verifier.EndTable(); + } +}; + +struct FieldBuilder +{ + typedef Field Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Field::VT_NAME, name); + } + void add_type(flatbuffers::Offset type) + { + fbb_.AddOffset(Field::VT_TYPE, type); + } + void add_id(uint16_t id) { fbb_.AddElement(Field::VT_ID, id, 0); } + void add_offset(uint16_t offset) { fbb_.AddElement(Field::VT_OFFSET, offset, 0); } + void add_default_integer(int64_t default_integer) + { + fbb_.AddElement(Field::VT_DEFAULT_INTEGER, default_integer, 0); + } + void add_default_real(double default_real) + { + fbb_.AddElement(Field::VT_DEFAULT_REAL, default_real, 0.0); + } + void add_deprecated(bool deprecated) + { + fbb_.AddElement(Field::VT_DEPRECATED, static_cast(deprecated), 0); + } + void add_required(bool required) + { + fbb_.AddElement(Field::VT_REQUIRED, static_cast(required), 0); + } + void add_key(bool key) { fbb_.AddElement(Field::VT_KEY, static_cast(key), 0); } + void add_attributes( + flatbuffers::Offset>> attributes) + { + fbb_.AddOffset(Field::VT_ATTRIBUTES, attributes); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(Field::VT_DOCUMENTATION, documentation); + } + void add_optional(bool optional) + { + fbb_.AddElement(Field::VT_OPTIONAL, static_cast(optional), 0); + } + explicit FieldBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, Field::VT_NAME); + fbb_.Required(o, Field::VT_TYPE); + return o; + } +}; + +inline flatbuffers::Offset CreateField( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + flatbuffers::Offset type = 0, uint16_t id = 0, uint16_t offset = 0, + int64_t default_integer = 0, double default_real = 0.0, bool deprecated = false, + bool required = false, bool key = false, + flatbuffers::Offset>> attributes = + 0, + flatbuffers::Offset>> documentation = + 0, + bool optional = false) +{ + FieldBuilder builder_(_fbb); + builder_.add_default_real(default_real); + builder_.add_default_integer(default_integer); + builder_.add_documentation(documentation); + builder_.add_attributes(attributes); + builder_.add_type(type); + builder_.add_name(name); + builder_.add_offset(offset); + builder_.add_id(id); + builder_.add_optional(optional); + builder_.add_key(key); + builder_.add_required(required); + builder_.add_deprecated(deprecated); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateFieldDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, + flatbuffers::Offset type = 0, uint16_t id = 0, uint16_t offset = 0, + int64_t default_integer = 0, double default_real = 0.0, bool deprecated = false, + bool required = false, bool key = false, + std::vector> *attributes = nullptr, + const std::vector> *documentation = nullptr, + bool optional = false) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto attributes__ = + attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateField(_fbb, name__, type, id, offset, default_integer, default_real, + deprecated, required, key, attributes__, documentation__, + optional); +} + +struct Object FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ObjectBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_FIELDS = 6, + VT_IS_STRUCT = 8, + VT_MINALIGN = 10, + VT_BYTESIZE = 12, + VT_ATTRIBUTES = 14, + VT_DOCUMENTATION = 16 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + bool KeyCompareLessThan(const Object *o) const { return *name() < *o->name(); } + int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); } + const flatbuffers::Vector> *fields() const + { + return GetPointer> *>( + VT_FIELDS); + } + bool is_struct() const { return GetField(VT_IS_STRUCT, 0) != 0; } + int32_t minalign() const { return GetField(VT_MINALIGN, 0); } + int32_t bytesize() const { return GetField(VT_BYTESIZE, 0); } + const flatbuffers::Vector> *attributes() const + { + return GetPointer> *>( + VT_ATTRIBUTES); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffsetRequired(verifier, VT_FIELDS) && + verifier.VerifyVector(fields()) && verifier.VerifyVectorOfTables(fields()) && + VerifyField(verifier, VT_IS_STRUCT) && + VerifyField(verifier, VT_MINALIGN) && + VerifyField(verifier, VT_BYTESIZE) && VerifyOffset(verifier, VT_ATTRIBUTES) && + verifier.VerifyVector(attributes()) && verifier.VerifyVectorOfTables(attributes()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && verifier.EndTable(); + } +}; + +struct ObjectBuilder +{ + typedef Object Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Object::VT_NAME, name); + } + void add_fields( + flatbuffers::Offset>> fields) + { + fbb_.AddOffset(Object::VT_FIELDS, fields); + } + void add_is_struct(bool is_struct) + { + fbb_.AddElement(Object::VT_IS_STRUCT, static_cast(is_struct), 0); + } + void add_minalign(int32_t minalign) + { + fbb_.AddElement(Object::VT_MINALIGN, minalign, 0); + } + void add_bytesize(int32_t bytesize) + { + fbb_.AddElement(Object::VT_BYTESIZE, bytesize, 0); + } + void add_attributes( + flatbuffers::Offset>> attributes) + { + fbb_.AddOffset(Object::VT_ATTRIBUTES, attributes); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(Object::VT_DOCUMENTATION, documentation); + } + explicit ObjectBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, Object::VT_NAME); + fbb_.Required(o, Object::VT_FIELDS); + return o; + } +}; + +inline flatbuffers::Offset CreateObject( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + flatbuffers::Offset>> fields = 0, + bool is_struct = false, int32_t minalign = 0, int32_t bytesize = 0, + flatbuffers::Offset>> attributes = + 0, + flatbuffers::Offset>> documentation = + 0) +{ + ObjectBuilder builder_(_fbb); + builder_.add_documentation(documentation); + builder_.add_attributes(attributes); + builder_.add_bytesize(bytesize); + builder_.add_minalign(minalign); + builder_.add_fields(fields); + builder_.add_name(name); + builder_.add_is_struct(is_struct); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateObjectDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, + std::vector> *fields = nullptr, bool is_struct = false, + int32_t minalign = 0, int32_t bytesize = 0, + std::vector> *attributes = nullptr, + const std::vector> *documentation = nullptr) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto fields__ = fields ? _fbb.CreateVectorOfSortedTables(fields) : 0; + auto attributes__ = + attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateObject(_fbb, name__, fields__, is_struct, minalign, bytesize, + attributes__, documentation__); +} + +struct RPCCall FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef RPCCallBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_REQUEST = 6, + VT_RESPONSE = 8, + VT_ATTRIBUTES = 10, + VT_DOCUMENTATION = 12 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + bool KeyCompareLessThan(const RPCCall *o) const { return *name() < *o->name(); } + int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); } + const reflection::Object *request() const + { + return GetPointer(VT_REQUEST); + } + const reflection::Object *response() const + { + return GetPointer(VT_RESPONSE); + } + const flatbuffers::Vector> *attributes() const + { + return GetPointer> *>( + VT_ATTRIBUTES); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffsetRequired(verifier, VT_REQUEST) && + verifier.VerifyTable(request()) && VerifyOffsetRequired(verifier, VT_RESPONSE) && + verifier.VerifyTable(response()) && VerifyOffset(verifier, VT_ATTRIBUTES) && + verifier.VerifyVector(attributes()) && verifier.VerifyVectorOfTables(attributes()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && verifier.EndTable(); + } +}; + +struct RPCCallBuilder +{ + typedef RPCCall Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(RPCCall::VT_NAME, name); + } + void add_request(flatbuffers::Offset request) + { + fbb_.AddOffset(RPCCall::VT_REQUEST, request); + } + void add_response(flatbuffers::Offset response) + { + fbb_.AddOffset(RPCCall::VT_RESPONSE, response); + } + void add_attributes( + flatbuffers::Offset>> attributes) + { + fbb_.AddOffset(RPCCall::VT_ATTRIBUTES, attributes); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(RPCCall::VT_DOCUMENTATION, documentation); + } + explicit RPCCallBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, RPCCall::VT_NAME); + fbb_.Required(o, RPCCall::VT_REQUEST); + fbb_.Required(o, RPCCall::VT_RESPONSE); + return o; + } +}; + +inline flatbuffers::Offset CreateRPCCall( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + flatbuffers::Offset request = 0, + flatbuffers::Offset response = 0, + flatbuffers::Offset>> attributes = + 0, + flatbuffers::Offset>> documentation = + 0) +{ + RPCCallBuilder builder_(_fbb); + builder_.add_documentation(documentation); + builder_.add_attributes(attributes); + builder_.add_response(response); + builder_.add_request(request); + builder_.add_name(name); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRPCCallDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, + flatbuffers::Offset request = 0, + flatbuffers::Offset response = 0, + std::vector> *attributes = nullptr, + const std::vector> *documentation = nullptr) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto attributes__ = + attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateRPCCall(_fbb, name__, request, response, attributes__, documentation__); +} + +struct Service FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ServiceBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_CALLS = 6, + VT_ATTRIBUTES = 8, + VT_DOCUMENTATION = 10 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + bool KeyCompareLessThan(const Service *o) const { return *name() < *o->name(); } + int KeyCompareWithValue(const char *val) const { return strcmp(name()->c_str(), val); } + const flatbuffers::Vector> *calls() const + { + return GetPointer> *>( + VT_CALLS); + } + const flatbuffers::Vector> *attributes() const + { + return GetPointer> *>( + VT_ATTRIBUTES); + } + const flatbuffers::Vector> *documentation() const + { + return GetPointer> *>( + VT_DOCUMENTATION); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffset(verifier, VT_CALLS) && + verifier.VerifyVector(calls()) && verifier.VerifyVectorOfTables(calls()) && + VerifyOffset(verifier, VT_ATTRIBUTES) && verifier.VerifyVector(attributes()) && + verifier.VerifyVectorOfTables(attributes()) && + VerifyOffset(verifier, VT_DOCUMENTATION) && verifier.VerifyVector(documentation()) && + verifier.VerifyVectorOfStrings(documentation()) && verifier.EndTable(); + } +}; + +struct ServiceBuilder +{ + typedef Service Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Service::VT_NAME, name); + } + void add_calls( + flatbuffers::Offset>> calls) + { + fbb_.AddOffset(Service::VT_CALLS, calls); + } + void add_attributes( + flatbuffers::Offset>> attributes) + { + fbb_.AddOffset(Service::VT_ATTRIBUTES, attributes); + } + void add_documentation( + flatbuffers::Offset>> + documentation) + { + fbb_.AddOffset(Service::VT_DOCUMENTATION, documentation); + } + explicit ServiceBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, Service::VT_NAME); + return o; + } +}; + +inline flatbuffers::Offset CreateService( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset name = 0, + flatbuffers::Offset>> calls = 0, + flatbuffers::Offset>> attributes = + 0, + flatbuffers::Offset>> documentation = + 0) +{ + ServiceBuilder builder_(_fbb); + builder_.add_documentation(documentation); + builder_.add_attributes(attributes); + builder_.add_calls(calls); + builder_.add_name(name); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateServiceDirect( + flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, + std::vector> *calls = nullptr, + std::vector> *attributes = nullptr, + const std::vector> *documentation = nullptr) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + auto calls__ = calls ? _fbb.CreateVectorOfSortedTables(calls) : 0; + auto attributes__ = + attributes ? _fbb.CreateVectorOfSortedTables(attributes) : 0; + auto documentation__ = + documentation ? _fbb.CreateVector>(*documentation) : 0; + return reflection::CreateService(_fbb, name__, calls__, attributes__, documentation__); +} + +struct Schema FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SchemaBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_OBJECTS = 4, + VT_ENUMS = 6, + VT_FILE_IDENT = 8, + VT_FILE_EXT = 10, + VT_ROOT_TABLE = 12, + VT_SERVICES = 14, + VT_ADVANCED_FEATURES = 16 + }; + const flatbuffers::Vector> *objects() const + { + return GetPointer> *>( + VT_OBJECTS); + } + const flatbuffers::Vector> *enums() const + { + return GetPointer> *>(VT_ENUMS); + } + const flatbuffers::String *file_ident() const + { + return GetPointer(VT_FILE_IDENT); + } + const flatbuffers::String *file_ext() const + { + return GetPointer(VT_FILE_EXT); + } + const reflection::Object *root_table() const + { + return GetPointer(VT_ROOT_TABLE); + } + const flatbuffers::Vector> *services() const + { + return GetPointer> *>( + VT_SERVICES); + } + reflection::AdvancedFeatures advanced_features() const + { + return static_cast(GetField(VT_ADVANCED_FEATURES, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffsetRequired(verifier, VT_OBJECTS) && + verifier.VerifyVector(objects()) && verifier.VerifyVectorOfTables(objects()) && + VerifyOffsetRequired(verifier, VT_ENUMS) && verifier.VerifyVector(enums()) && + verifier.VerifyVectorOfTables(enums()) && VerifyOffset(verifier, VT_FILE_IDENT) && + verifier.VerifyString(file_ident()) && VerifyOffset(verifier, VT_FILE_EXT) && + verifier.VerifyString(file_ext()) && VerifyOffset(verifier, VT_ROOT_TABLE) && + verifier.VerifyTable(root_table()) && VerifyOffset(verifier, VT_SERVICES) && + verifier.VerifyVector(services()) && verifier.VerifyVectorOfTables(services()) && + VerifyField(verifier, VT_ADVANCED_FEATURES) && verifier.EndTable(); + } +}; + +struct SchemaBuilder +{ + typedef Schema Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_objects( + flatbuffers::Offset>> objects) + { + fbb_.AddOffset(Schema::VT_OBJECTS, objects); + } + void + add_enums(flatbuffers::Offset>> enums) + { + fbb_.AddOffset(Schema::VT_ENUMS, enums); + } + void add_file_ident(flatbuffers::Offset file_ident) + { + fbb_.AddOffset(Schema::VT_FILE_IDENT, file_ident); + } + void add_file_ext(flatbuffers::Offset file_ext) + { + fbb_.AddOffset(Schema::VT_FILE_EXT, file_ext); + } + void add_root_table(flatbuffers::Offset root_table) + { + fbb_.AddOffset(Schema::VT_ROOT_TABLE, root_table); + } + void add_services( + flatbuffers::Offset>> services) + { + fbb_.AddOffset(Schema::VT_SERVICES, services); + } + void add_advanced_features(reflection::AdvancedFeatures advanced_features) + { + fbb_.AddElement(Schema::VT_ADVANCED_FEATURES, + static_cast(advanced_features), 0); + } + explicit SchemaBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + fbb_.Required(o, Schema::VT_OBJECTS); + fbb_.Required(o, Schema::VT_ENUMS); + return o; + } +}; + +inline flatbuffers::Offset CreateSchema( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> objects = 0, + flatbuffers::Offset>> enums = 0, + flatbuffers::Offset file_ident = 0, + flatbuffers::Offset file_ext = 0, + flatbuffers::Offset root_table = 0, + flatbuffers::Offset>> services = 0, + reflection::AdvancedFeatures advanced_features = static_cast(0)) +{ + SchemaBuilder builder_(_fbb); + builder_.add_advanced_features(advanced_features); + builder_.add_services(services); + builder_.add_root_table(root_table); + builder_.add_file_ext(file_ext); + builder_.add_file_ident(file_ident); + builder_.add_enums(enums); + builder_.add_objects(objects); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSchemaDirect( + flatbuffers::FlatBufferBuilder &_fbb, + std::vector> *objects = nullptr, + std::vector> *enums = nullptr, + const char *file_ident = nullptr, const char *file_ext = nullptr, + flatbuffers::Offset root_table = 0, + std::vector> *services = nullptr, + reflection::AdvancedFeatures advanced_features = static_cast(0)) +{ + auto objects__ = objects ? _fbb.CreateVectorOfSortedTables(objects) : 0; + auto enums__ = enums ? _fbb.CreateVectorOfSortedTables(enums) : 0; + auto file_ident__ = file_ident ? _fbb.CreateString(file_ident) : 0; + auto file_ext__ = file_ext ? _fbb.CreateString(file_ext) : 0; + auto services__ = services ? _fbb.CreateVectorOfSortedTables(services) : 0; + return reflection::CreateSchema(_fbb, objects__, enums__, file_ident__, file_ext__, root_table, + services__, advanced_features); +} + +inline const reflection::Schema *GetSchema(const void *buf) +{ + return flatbuffers::GetRoot(buf); +} + +inline const reflection::Schema *GetSizePrefixedSchema(const void *buf) +{ + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline const char *SchemaIdentifier() { return "BFBS"; } + +inline bool SchemaBufferHasIdentifier(const void *buf) +{ + return flatbuffers::BufferHasIdentifier(buf, SchemaIdentifier()); +} + +inline bool VerifySchemaBuffer(flatbuffers::Verifier &verifier) +{ + return verifier.VerifyBuffer(SchemaIdentifier()); +} + +inline bool VerifySizePrefixedSchemaBuffer(flatbuffers::Verifier &verifier) +{ + return verifier.VerifySizePrefixedBuffer(SchemaIdentifier()); +} + +inline const char *SchemaExtension() { return "bfbs"; } + +inline void FinishSchemaBuffer(flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) +{ + fbb.Finish(root, SchemaIdentifier()); +} + +inline void FinishSizePrefixedSchemaBuffer(flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) +{ + fbb.FinishSizePrefixed(root, SchemaIdentifier()); +} + +} // namespace reflection + +#endif // FLATBUFFERS_GENERATED_REFLECTION_REFLECTION_H_ diff --git a/onert-micro/externals/flatbuffers/registry.h b/onert-micro/externals/flatbuffers/registry.h new file mode 100644 index 0000000..c06bd56 --- /dev/null +++ b/onert-micro/externals/flatbuffers/registry.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_REGISTRY_H_ +#define FLATBUFFERS_REGISTRY_H_ + +#include "flatbuffers/idl.h" + +namespace flatbuffers +{ + +// Convenience class to easily parse or generate text for arbitrary FlatBuffers. +// Simply pre-populate it with all schema filenames that may be in use, and +// This class will look them up using the file_identifier declared in the +// schema. +class Registry +{ +public: + // Call this for all schemas that may be in use. The identifier has + // a function in the generated code, e.g. MonsterIdentifier(). + void Register(const char *file_identifier, const char *schema_path) + { + Schema schema; + schema.path_ = schema_path; + schemas_[file_identifier] = schema; + } + + // Generate text from an arbitrary FlatBuffer by looking up its + // file_identifier in the registry. + bool FlatBufferToText(const uint8_t *flatbuf, size_t len, std::string *dest) + { + // Get the identifier out of the buffer. + // If the buffer is truncated, exit. + if (len < sizeof(uoffset_t) + FlatBufferBuilder::kFileIdentifierLength) + { + lasterror_ = "buffer truncated"; + return false; + } + std::string ident(reinterpret_cast(flatbuf) + sizeof(uoffset_t), + FlatBufferBuilder::kFileIdentifierLength); + // Load and parse the schema. + Parser parser; + if (!LoadSchema(ident, &parser)) + return false; + // Now we're ready to generate text. + if (!GenerateText(parser, flatbuf, dest)) + { + lasterror_ = "unable to generate text for FlatBuffer binary"; + return false; + } + return true; + } + + // Converts a binary buffer to text using one of the schemas in the registry, + // use the file_identifier to indicate which. + // If DetachedBuffer::data() is null then parsing failed. + DetachedBuffer TextToFlatBuffer(const char *text, const char *file_identifier) + { + // Load and parse the schema. + Parser parser; + if (!LoadSchema(file_identifier, &parser)) + return DetachedBuffer(); + // Parse the text. + if (!parser.Parse(text)) + { + lasterror_ = parser.error_; + return DetachedBuffer(); + } + // We have a valid FlatBuffer. Detach it from the builder and return. + return parser.builder_.Release(); + } + + // Modify any parsing / output options used by the other functions. + void SetOptions(const IDLOptions &opts) { opts_ = opts; } + + // If schemas used contain include statements, call this function for every + // directory the parser should search them for. + void AddIncludeDirectory(const char *path) { include_paths_.push_back(path); } + + // Returns a human readable error if any of the above functions fail. + const std::string &GetLastError() { return lasterror_; } + +private: + bool LoadSchema(const std::string &ident, Parser *parser) + { + // Find the schema, if not, exit. + auto it = schemas_.find(ident); + if (it == schemas_.end()) + { + // Don't attach the identifier, since it may not be human readable. + lasterror_ = "identifier for this buffer not in the registry"; + return false; + } + auto &schema = it->second; + // Load the schema from disk. If not, exit. + std::string schematext; + if (!LoadFile(schema.path_.c_str(), false, &schematext)) + { + lasterror_ = "could not load schema: " + schema.path_; + return false; + } + // Parse schema. + parser->opts = opts_; + if (!parser->Parse(schematext.c_str(), vector_data(include_paths_), schema.path_.c_str())) + { + lasterror_ = parser->error_; + return false; + } + return true; + } + + struct Schema + { + std::string path_; + // TODO(wvo) optionally cache schema file or parsed schema here. + }; + + std::string lasterror_; + IDLOptions opts_; + std::vector include_paths_; + std::map schemas_; +}; + +} // namespace flatbuffers + +#endif // FLATBUFFERS_REGISTRY_H_ diff --git a/onert-micro/externals/flatbuffers/stl_emulation.h b/onert-micro/externals/flatbuffers/stl_emulation.h new file mode 100644 index 0000000..3f11fb9 --- /dev/null +++ b/onert-micro/externals/flatbuffers/stl_emulation.h @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_STL_EMULATION_H_ +#define FLATBUFFERS_STL_EMULATION_H_ + +// clang-format off +#include "flatbuffers/base.h" + +#include +#include +#include +#include +#include + +#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) + #define FLATBUFFERS_CPP98_STL +#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) + +#if defined(FLATBUFFERS_CPP98_STL) + #include +#endif // defined(FLATBUFFERS_CPP98_STL) + +// Detect C++17 compatible compiler. +// __cplusplus >= 201703L - a compiler has support of 'static inline' variables. +#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \ + || (defined(__cplusplus) && __cplusplus >= 201703L) \ + || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)) + #include + #ifndef FLATBUFFERS_USE_STD_OPTIONAL + #define FLATBUFFERS_USE_STD_OPTIONAL + #endif +#endif // defined(FLATBUFFERS_USE_STD_OPTIONAL) ... + +// The __cpp_lib_span is the predefined feature macro. +#if defined(FLATBUFFERS_USE_STD_SPAN) + #include +#elif defined(__cpp_lib_span) && defined(__has_include) + #if __has_include() + #include + #define FLATBUFFERS_USE_STD_SPAN + #endif +#else + // Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined. + #if !defined(FLATBUFFERS_TEMPLATES_ALIASES) || defined(FLATBUFFERS_CPP98_STL) + #define FLATBUFFERS_SPAN_MINIMAL + #else + // Enable implicit construction of a span from a std::array. + #include + #endif +#endif // defined(FLATBUFFERS_USE_STD_SPAN) + +// This header provides backwards compatibility for C++98 STLs like stlport. +namespace flatbuffers { + +// Retrieve ::back() from a string in a way that is compatible with pre C++11 +// STLs (e.g stlport). +inline char& string_back(std::string &value) { + return value[value.length() - 1]; +} + +inline char string_back(const std::string &value) { + return value[value.length() - 1]; +} + +// Helper method that retrieves ::data() from a vector in a way that is +// compatible with pre C++11 STLs (e.g stlport). +template inline T *vector_data(std::vector &vector) { + // In some debug environments, operator[] does bounds checking, so &vector[0] + // can't be used. + return vector.empty() ? nullptr : &vector[0]; +} + +template inline const T *vector_data( + const std::vector &vector) { + return vector.empty() ? nullptr : &vector[0]; +} + +template +inline void vector_emplace_back(std::vector *vector, V &&data) { + #if defined(FLATBUFFERS_CPP98_STL) + vector->push_back(data); + #else + vector->emplace_back(std::forward(data)); + #endif // defined(FLATBUFFERS_CPP98_STL) +} + +#ifndef FLATBUFFERS_CPP98_STL + #if defined(FLATBUFFERS_TEMPLATES_ALIASES) + template + using numeric_limits = std::numeric_limits; + #else + template class numeric_limits : + public std::numeric_limits {}; + #endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) +#else + template class numeric_limits : + public std::numeric_limits { + public: + // Android NDK fix. + static T lowest() { + return std::numeric_limits::min(); + } + }; + + template <> class numeric_limits : + public std::numeric_limits { + public: + static float lowest() { return -FLT_MAX; } + }; + + template <> class numeric_limits : + public std::numeric_limits { + public: + static double lowest() { return -DBL_MAX; } + }; + + template <> class numeric_limits { + public: + static unsigned long long min() { return 0ULL; } + static unsigned long long max() { return ~0ULL; } + static unsigned long long lowest() { + return numeric_limits::min(); + } + }; + + template <> class numeric_limits { + public: + static long long min() { + return static_cast(1ULL << ((sizeof(long long) << 3) - 1)); + } + static long long max() { + return static_cast( + (1ULL << ((sizeof(long long) << 3) - 1)) - 1); + } + static long long lowest() { + return numeric_limits::min(); + } + }; +#endif // FLATBUFFERS_CPP98_STL + +#if defined(FLATBUFFERS_TEMPLATES_ALIASES) + #ifndef FLATBUFFERS_CPP98_STL + template using is_scalar = std::is_scalar; + template using is_same = std::is_same; + template using is_floating_point = std::is_floating_point; + template using is_unsigned = std::is_unsigned; + template using is_enum = std::is_enum; + template using make_unsigned = std::make_unsigned; + template + using conditional = std::conditional; + template + using integral_constant = std::integral_constant; + template + using bool_constant = integral_constant; + #else + // Map C++ TR1 templates defined by stlport. + template using is_scalar = std::tr1::is_scalar; + template using is_same = std::tr1::is_same; + template using is_floating_point = + std::tr1::is_floating_point; + template using is_unsigned = std::tr1::is_unsigned; + template using is_enum = std::tr1::is_enum; + // Android NDK doesn't have std::make_unsigned or std::tr1::make_unsigned. + template struct make_unsigned { + static_assert(is_unsigned::value, "Specialization not implemented!"); + using type = T; + }; + template<> struct make_unsigned { using type = unsigned char; }; + template<> struct make_unsigned { using type = unsigned short; }; + template<> struct make_unsigned { using type = unsigned int; }; + template<> struct make_unsigned { using type = unsigned long; }; + template<> + struct make_unsigned { using type = unsigned long long; }; + template + using conditional = std::tr1::conditional; + template + using integral_constant = std::tr1::integral_constant; + template + using bool_constant = integral_constant; + #endif // !FLATBUFFERS_CPP98_STL +#else + // MSVC 2010 doesn't support C++11 aliases. + template struct is_scalar : public std::is_scalar {}; + template struct is_same : public std::is_same {}; + template struct is_floating_point : + public std::is_floating_point {}; + template struct is_unsigned : public std::is_unsigned {}; + template struct is_enum : public std::is_enum {}; + template struct make_unsigned : public std::make_unsigned {}; + template + struct conditional : public std::conditional {}; + template + struct integral_constant : public std::integral_constant {}; + template + struct bool_constant : public integral_constant {}; +#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) + +#ifndef FLATBUFFERS_CPP98_STL + #if defined(FLATBUFFERS_TEMPLATES_ALIASES) + template using unique_ptr = std::unique_ptr; + #else + // MSVC 2010 doesn't support C++11 aliases. + // We're manually "aliasing" the class here as we want to bring unique_ptr + // into the flatbuffers namespace. We have unique_ptr in the flatbuffers + // namespace we have a completely independent implementation (see below) + // for C++98 STL implementations. + template class unique_ptr : public std::unique_ptr { + public: + unique_ptr() {} + explicit unique_ptr(T* p) : std::unique_ptr(p) {} + unique_ptr(std::unique_ptr&& u) { *this = std::move(u); } + unique_ptr(unique_ptr&& u) { *this = std::move(u); } + unique_ptr& operator=(std::unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(T* p) { + return std::unique_ptr::operator=(p); + } + }; + #endif // defined(FLATBUFFERS_TEMPLATES_ALIASES) +#else + // Very limited implementation of unique_ptr. + // This is provided simply to allow the C++ code generated from the default + // settings to function in C++98 environments with no modifications. + template class unique_ptr { + public: + typedef T element_type; + + unique_ptr() : ptr_(nullptr) {} + explicit unique_ptr(T* p) : ptr_(p) {} + unique_ptr(unique_ptr&& u) : ptr_(nullptr) { reset(u.release()); } + unique_ptr(const unique_ptr& u) : ptr_(nullptr) { + reset(const_cast(&u)->release()); + } + ~unique_ptr() { reset(); } + + unique_ptr& operator=(const unique_ptr& u) { + reset(const_cast(&u)->release()); + return *this; + } + + unique_ptr& operator=(unique_ptr&& u) { + reset(u.release()); + return *this; + } + + unique_ptr& operator=(T* p) { + reset(p); + return *this; + } + + const T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const noexcept { return ptr_; } + explicit operator bool() const { return ptr_ != nullptr; } + + // modifiers + T* release() { + T* value = ptr_; + ptr_ = nullptr; + return value; + } + + void reset(T* p = nullptr) { + T* value = ptr_; + ptr_ = p; + if (value) delete value; + } + + void swap(unique_ptr& u) { + T* temp_ptr = ptr_; + ptr_ = u.ptr_; + u.ptr_ = temp_ptr; + } + + private: + T* ptr_; + }; + + template bool operator==(const unique_ptr& x, + const unique_ptr& y) { + return x.get() == y.get(); + } + + template bool operator==(const unique_ptr& x, + const D* y) { + return static_cast(x.get()) == y; + } + + template bool operator==(const unique_ptr& x, intptr_t y) { + return reinterpret_cast(x.get()) == y; + } + + template bool operator!=(const unique_ptr& x, decltype(nullptr)) { + return !!x; + } + + template bool operator!=(decltype(nullptr), const unique_ptr& x) { + return !!x; + } + + template bool operator==(const unique_ptr& x, decltype(nullptr)) { + return !x; + } + + template bool operator==(decltype(nullptr), const unique_ptr& x) { + return !x; + } + +#endif // !FLATBUFFERS_CPP98_STL + +#ifdef FLATBUFFERS_USE_STD_OPTIONAL +template +using Optional = std::optional; +using nullopt_t = std::nullopt_t; +inline constexpr nullopt_t nullopt = std::nullopt; + +#else +// Limited implementation of Optional type for a scalar T. +// This implementation limited by trivial types compatible with +// std::is_arithmetic or std::is_enum type traits. + +// A tag to indicate an empty flatbuffers::optional. +struct nullopt_t { + explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {} +}; + +#if defined(FLATBUFFERS_CONSTEXPR_DEFINED) + namespace internal { + template struct nullopt_holder { + static constexpr nullopt_t instance_ = nullopt_t(0); + }; + template + constexpr nullopt_t nullopt_holder::instance_; + } + static constexpr const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#else + namespace internal { + template struct nullopt_holder { + static const nullopt_t instance_; + }; + template + const nullopt_t nullopt_holder::instance_ = nullopt_t(0); + } + static const nullopt_t &nullopt = internal::nullopt_holder::instance_; + +#endif + +template +class Optional FLATBUFFERS_FINAL_CLASS { + // Non-scalar 'T' would extremely complicated Optional. + // Use is_scalar checking because flatbuffers flatbuffers::is_arithmetic + // isn't implemented. + static_assert(flatbuffers::is_scalar::value, "unexpected type T"); + + public: + ~Optional() {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT + : value_(), has_value_(false) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT + : value_(val), has_value_(true) {} + + FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT + : value_(other.value_), has_value_(other.has_value_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT { + value_ = other.value_; + has_value_ = other.has_value_; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT { + value_ = T(); + has_value_ = false; + return *this; + } + + FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT { + value_ = val; + has_value_ = true; + return *this; + } + + void reset() FLATBUFFERS_NOEXCEPT { + *this = nullopt; + } + + void swap(Optional &other) FLATBUFFERS_NOEXCEPT { + std::swap(value_, other.value_); + std::swap(has_value_, other.has_value_); + } + + FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT { + return has_value_; + } + + FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT { + return value_; + } + + const T& value() const { + FLATBUFFERS_ASSERT(has_value()); + return value_; + } + + T value_or(T default_value) const FLATBUFFERS_NOEXCEPT { + return has_value() ? value_ : default_value; + } + + private: + T value_; + bool has_value_; +}; + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& opt, nullopt_t) FLATBUFFERS_NOEXCEPT { + return !opt; +} +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional& opt) FLATBUFFERS_NOEXCEPT { + return !opt; +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) && (*lhs == rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(rhs) && (lhs == *rhs); +} + +template +FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional& lhs, const Optional& rhs) FLATBUFFERS_NOEXCEPT { + return static_cast(lhs) != static_cast(rhs) + ? false + : !static_cast(lhs) ? false : (*lhs == *rhs); +} +#endif // FLATBUFFERS_USE_STD_OPTIONAL + + +// Very limited and naive partial implementation of C++20 std::span. +#if defined(FLATBUFFERS_USE_STD_SPAN) + inline constexpr std::size_t dynamic_extent = std::dynamic_extent; + template + using span = std::span; + +#else // !defined(FLATBUFFERS_USE_STD_SPAN) +FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast(-1); + +// Exclude this code if MSVC2010 or non-STL Android is active. +// The non-STL Android doesn't have `std::is_convertible` required for SFINAE. +#if !defined(FLATBUFFERS_SPAN_MINIMAL) +namespace internal { + // This is SFINAE helper class for checking of a common condition: + // > This overload only participates in overload resolution + // > Check whether a pointer to an array of U can be converted + // > to a pointer to an array of E. + // This helper is used for checking of 'U -> const U'. + template + struct is_span_convertable { + using type = + typename std::conditional::value + && (Extent == dynamic_extent || N == Extent), + int, void>::type; + }; + +} // namespace internal +#endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + +// T - element type; must be a complete type that is not an abstract +// class type. +// Extent - the number of elements in the sequence, or dynamic. +template +class span FLATBUFFERS_FINAL_CLASS { + public: + typedef T element_type; + typedef T& reference; + typedef const T& const_reference; + typedef T* pointer; + typedef const T* const_pointer; + typedef std::size_t size_type; + + static FLATBUFFERS_CONSTEXPR size_type extent = Extent; + + // Returns the number of elements in the span. + FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT { + return count_; + } + + // Returns the size of the sequence in bytes. + FLATBUFFERS_CONSTEXPR_CPP11 + size_type size_bytes() const FLATBUFFERS_NOEXCEPT { + return size() * sizeof(element_type); + } + + // Checks if the span is empty. + FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT { + return size() == 0; + } + + // Returns a pointer to the beginning of the sequence. + FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT { + return data_; + } + + // Returns a reference to the idx-th element of the sequence. + // The behavior is undefined if the idx is greater than or equal to size(). + FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const { + return data()[idx]; + } + + FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT + : data_(other.data_), count_(other.count_) {} + + FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other) + FLATBUFFERS_NOEXCEPT { + data_ = other.data_; + count_ = other.count_; + } + + // Limited implementation of + // `template constexpr std::span(It first, size_type count);`. + // + // Constructs a span that is a view over the range [first, first + count); + // the resulting span has: data() == first and size() == count. + // The behavior is undefined if [first, first + count) is not a valid range, + // or if (extent != flatbuffers::dynamic_extent && count != extent). + FLATBUFFERS_CONSTEXPR_CPP11 + explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT + : data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)), + count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) { + // Make span empty if the count argument is incompatible with span. + } + + // Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11 + // compliant, it doesn't support default template arguments for functions. + #if defined(FLATBUFFERS_SPAN_MINIMAL) + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + #else + // Constructs an empty span whose data() == nullptr and size() == 0. + // This overload only participates in overload resolution if + // extent == 0 || extent == flatbuffers::dynamic_extent. + // A dummy template argument N is need dependency for SFINAE. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr), + count_(0) { + static_assert(extent == 0 || extent == dynamic_extent, "invalid span"); + } + + // Constructs a span that is a view over the array arr; the resulting span + // has size() == N and data() == std::data(arr). These overloads only + // participate in overload resolution if + // extent == std::dynamic_extent || N == extent is true and + // std::remove_pointer_t(*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT + : data_(arr), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + //template + //FLATBUFFERS_CONSTEXPR_CPP11 span(std::array &arr) FLATBUFFERS_NOEXCEPT + // : data_(arr.data()), count_(N) {} + + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array &arr) FLATBUFFERS_NOEXCEPT + : data_(arr.data()), count_(N) {} + + // Converting constructor from another span s; + // the resulting span has size() == s.size() and data() == s.data(). + // This overload only participates in overload resolution + // if extent == std::dynamic_extent || N == extent is true and U (*)[] + // is convertible to element_type (*)[]. + template::type = 0> + FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span &s) FLATBUFFERS_NOEXCEPT + : span(s.data(), s.size()) { + } + + #endif // !defined(FLATBUFFERS_SPAN_MINIMAL) + + private: + // This is a naive implementation with 'count_' member even if (Extent != dynamic_extent). + pointer const data_; + const size_type count_; +}; + + #if !defined(FLATBUFFERS_SPAN_MINIMAL) + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const U(&arr)[N]) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const std::array &arr) FLATBUFFERS_NOEXCEPT { + return span(arr); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); + } + + template + FLATBUFFERS_CONSTEXPR_CPP11 + flatbuffers::span make_span(const U *first, std::size_t count) FLATBUFFERS_NOEXCEPT { + return span(first, count); + } +#endif + +#endif // defined(FLATBUFFERS_USE_STD_SPAN) + +} // namespace flatbuffers + +#endif // FLATBUFFERS_STL_EMULATION_H_ diff --git a/onert-micro/externals/flatbuffers/util.h b/onert-micro/externals/flatbuffers/util.h new file mode 100644 index 0000000..e255801 --- /dev/null +++ b/onert-micro/externals/flatbuffers/util.h @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 FLATBUFFERS_UTIL_H_ +#define FLATBUFFERS_UTIL_H_ + +#include + +#include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" + +#ifndef FLATBUFFERS_PREFER_PRINTF +#include +#else // FLATBUFFERS_PREFER_PRINTF +#include +#include +#endif // FLATBUFFERS_PREFER_PRINTF + +#include +#include + +namespace flatbuffers +{ + +// @locale-independent functions for ASCII characters set. + +// Fast checking that character lies in closed range: [a <= x <= b] +// using one compare (conditional branch) operator. +inline bool check_ascii_range(char x, char a, char b) +{ + FLATBUFFERS_ASSERT(a <= b); + // (Hacker's Delight): `a <= x <= b` <=> `(x-a) <={u} (b-a)`. + // The x, a, b will be promoted to int and subtracted without overflow. + return static_cast(x - a) <= static_cast(b - a); +} + +// Case-insensitive isalpha +inline bool is_alpha(char c) +{ + // ASCII only: alpha to upper case => reset bit 0x20 (~0x20 = 0xDF). + return check_ascii_range(c & 0xDF, 'a' & 0xDF, 'z' & 0xDF); +} + +// Check for uppercase alpha +inline bool is_alpha_upper(char c) { return check_ascii_range(c, 'A', 'Z'); } + +// Check (case-insensitive) that `c` is equal to alpha. +inline bool is_alpha_char(char c, char alpha) +{ + FLATBUFFERS_ASSERT(is_alpha(alpha)); + // ASCII only: alpha to upper case => reset bit 0x20 (~0x20 = 0xDF). + return ((c & 0xDF) == (alpha & 0xDF)); +} + +// https://en.cppreference.com/w/cpp/string/byte/isxdigit +// isdigit and isxdigit are the only standard narrow character classification +// functions that are not affected by the currently installed C locale. although +// some implementations (e.g. Microsoft in 1252 codepage) may classify +// additional single-byte characters as digits. +inline bool is_digit(char c) { return check_ascii_range(c, '0', '9'); } + +inline bool is_xdigit(char c) +{ + // Replace by look-up table. + return is_digit(c) || check_ascii_range(c & 0xDF, 'a' & 0xDF, 'f' & 0xDF); +} + +// Case-insensitive isalnum +inline bool is_alnum(char c) { return is_alpha(c) || is_digit(c); } + +inline char CharToUpper(char c) +{ + return static_cast(::toupper(static_cast(c))); +} + +inline char CharToLower(char c) +{ + return static_cast(::tolower(static_cast(c))); +} + +// @end-locale-independent functions for ASCII character set + +#ifdef FLATBUFFERS_PREFER_PRINTF +template size_t IntToDigitCount(T t) +{ + size_t digit_count = 0; + // Count the sign for negative numbers + if (t < 0) + digit_count++; + // Count a single 0 left of the dot for fractional numbers + if (-1 < t && t < 1) + digit_count++; + // Count digits until fractional part + T eps = std::numeric_limits::epsilon(); + while (t <= (-1 + eps) || (1 - eps) <= t) + { + t /= 10; + digit_count++; + } + return digit_count; +} + +template size_t NumToStringWidth(T t, int precision = 0) +{ + size_t string_width = IntToDigitCount(t); + // Count the dot for floating point numbers + if (precision) + string_width += (precision + 1); + return string_width; +} + +template std::string NumToStringImplWrapper(T t, const char *fmt, int precision = 0) +{ + size_t string_width = NumToStringWidth(t, precision); + std::string s(string_width, 0x00); + // Allow snprintf to use std::string trailing null to detect buffer overflow + snprintf(const_cast(s.data()), (s.size() + 1), fmt, string_width, t); + return s; +} +#endif // FLATBUFFERS_PREFER_PRINTF + +// Convert an integer or floating point value to a string. +// In contrast to std::stringstream, "char" values are +// converted to a string of digits, and we don't use scientific notation. +template std::string NumToString(T t) +{ + // clang-format off + + #ifndef FLATBUFFERS_PREFER_PRINTF + std::stringstream ss; + ss << t; + return ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + auto v = static_cast(t); + return NumToStringImplWrapper(v, "%.*lld"); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on +} +// Avoid char types used as character data. +template <> inline std::string NumToString(signed char t) +{ + return NumToString(static_cast(t)); +} +template <> inline std::string NumToString(unsigned char t) +{ + return NumToString(static_cast(t)); +} +template <> inline std::string NumToString(char t) +{ + return NumToString(static_cast(t)); +} +#if defined(FLATBUFFERS_CPP98_STL) +template <> inline std::string NumToString(long long t) +{ + char buf[21]; // (log((1 << 63) - 1) / log(10)) + 2 + snprintf(buf, sizeof(buf), "%lld", t); + return std::string(buf); +} + +template <> inline std::string NumToString(unsigned long long t) +{ + char buf[22]; // (log((1 << 63) - 1) / log(10)) + 1 + snprintf(buf, sizeof(buf), "%llu", t); + return std::string(buf); +} +#endif // defined(FLATBUFFERS_CPP98_STL) + +// Special versions for floats/doubles. +template std::string FloatToString(T t, int precision) +{ + // clang-format off + + #ifndef FLATBUFFERS_PREFER_PRINTF + // to_string() prints different numbers of digits for floats depending on + // platform and isn't available on Android, so we use stringstream + std::stringstream ss; + // Use std::fixed to suppress scientific notation. + ss << std::fixed; + // Default precision is 6, we want that to be higher for doubles. + ss << std::setprecision(precision); + ss << t; + auto s = ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + auto v = static_cast(t); + auto s = NumToStringImplWrapper(v, "%0.*f", precision); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on + // Sadly, std::fixed turns "1" into "1.00000", so here we undo that. + auto p = s.find_last_not_of('0'); + if (p != std::string::npos) + { + // Strip trailing zeroes. If it is a whole number, keep one zero. + s.resize(p + (s[p] == '.' ? 2 : 1)); + } + return s; +} + +template <> inline std::string NumToString(double t) { return FloatToString(t, 12); } +template <> inline std::string NumToString(float t) { return FloatToString(t, 6); } + +// Convert an integer value to a hexadecimal string. +// The returned string length is always xdigits long, prefixed by 0 digits. +// For example, IntToStringHex(0x23, 8) returns the string "00000023". +inline std::string IntToStringHex(int i, int xdigits) +{ + FLATBUFFERS_ASSERT(i >= 0); + // clang-format off + + #ifndef FLATBUFFERS_PREFER_PRINTF + std::stringstream ss; + ss << std::setw(xdigits) << std::setfill('0') << std::hex << std::uppercase + << i; + return ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + return NumToStringImplWrapper(i, "%.*X", xdigits); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on +} + +// clang-format off +// Use locale independent functions {strtod_l, strtof_l, strtoll_l, strtoull_l}. +#if defined(FLATBUFFERS_LOCALE_INDEPENDENT) && (FLATBUFFERS_LOCALE_INDEPENDENT > 0) + class ClassicLocale { + #ifdef _MSC_VER + typedef _locale_t locale_type; + #else + typedef locale_t locale_type; // POSIX.1-2008 locale_t type + #endif + ClassicLocale(); + ~ClassicLocale(); + locale_type locale_; + static ClassicLocale instance_; + public: + static locale_type Get() { return instance_.locale_; } + }; + + #ifdef _MSC_VER + #define __strtoull_impl(s, pe, b) _strtoui64_l(s, pe, b, ClassicLocale::Get()) + #define __strtoll_impl(s, pe, b) _strtoi64_l(s, pe, b, ClassicLocale::Get()) + #define __strtod_impl(s, pe) _strtod_l(s, pe, ClassicLocale::Get()) + #define __strtof_impl(s, pe) _strtof_l(s, pe, ClassicLocale::Get()) + #else + #define __strtoull_impl(s, pe, b) strtoull_l(s, pe, b, ClassicLocale::Get()) + #define __strtoll_impl(s, pe, b) strtoll_l(s, pe, b, ClassicLocale::Get()) + #define __strtod_impl(s, pe) strtod_l(s, pe, ClassicLocale::Get()) + #define __strtof_impl(s, pe) strtof_l(s, pe, ClassicLocale::Get()) + #endif +#else + #define __strtod_impl(s, pe) strtod(s, pe) + #define __strtof_impl(s, pe) static_cast(strtod(s, pe)) + #ifdef _MSC_VER + #define __strtoull_impl(s, pe, b) _strtoui64(s, pe, b) + #define __strtoll_impl(s, pe, b) _strtoi64(s, pe, b) + #else + #define __strtoull_impl(s, pe, b) strtoull(s, pe, b) + #define __strtoll_impl(s, pe, b) strtoll(s, pe, b) + #endif +#endif + +inline void strtoval_impl(int64_t *val, const char *str, char **endptr, + int base) { + *val = __strtoll_impl(str, endptr, base); +} + +inline void strtoval_impl(uint64_t *val, const char *str, char **endptr, + int base) { + *val = __strtoull_impl(str, endptr, base); +} + +inline void strtoval_impl(double *val, const char *str, char **endptr) { + *val = __strtod_impl(str, endptr); +} + +// UBSAN: double to float is safe if numeric_limits::is_iec559 is true. +__supress_ubsan__("float-cast-overflow") +inline void strtoval_impl(float *val, const char *str, char **endptr) { + *val = __strtof_impl(str, endptr); +} +#undef __strtoull_impl +#undef __strtoll_impl +#undef __strtod_impl +#undef __strtof_impl +// clang-format on + +// Adaptor for strtoull()/strtoll(). +// Flatbuffers accepts numbers with any count of leading zeros (-009 is -9), +// while strtoll with base=0 interprets first leading zero as octal prefix. +// In future, it is possible to add prefixed 0b0101. +// 1) Checks errno code for overflow condition (out of range). +// 2) If base <= 0, function try to detect base of number by prefix. +// +// Return value (like strtoull and strtoll, but reject partial result): +// - If successful, an integer value corresponding to the str is returned. +// - If full string conversion can't be performed, 0 is returned. +// - If the converted value falls out of range of corresponding return type, a +// range error occurs. In this case value MAX(T)/MIN(T) is returned. +template +inline bool StringToIntegerImpl(T *val, const char *const str, const int base = 0, + const bool check_errno = true) +{ + // T is int64_t or uint64_T + FLATBUFFERS_ASSERT(str); + if (base <= 0) + { + auto s = str; + while (*s && !is_digit(*s)) + s++; + if (s[0] == '0' && is_alpha_char(s[1], 'X')) + return StringToIntegerImpl(val, str, 16, check_errno); + // if a prefix not match, try base=10 + return StringToIntegerImpl(val, str, 10, check_errno); + } + else + { + if (check_errno) + errno = 0; // clear thread-local errno + auto endptr = str; + strtoval_impl(val, str, const_cast(&endptr), base); + if ((*endptr != '\0') || (endptr == str)) + { + *val = 0; // erase partial result + return false; // invalid string + } + // errno is out-of-range, return MAX/MIN + if (check_errno && errno) + return false; + return true; + } +} + +template inline bool StringToFloatImpl(T *val, const char *const str) +{ + // Type T must be either float or double. + FLATBUFFERS_ASSERT(str && val); + auto end = str; + strtoval_impl(val, str, const_cast(&end)); + auto done = (end != str) && (*end == '\0'); + if (!done) + *val = 0; // erase partial result + return done; +} + +// Convert a string to an instance of T. +// Return value (matched with StringToInteger64Impl and strtod): +// - If successful, a numeric value corresponding to the str is returned. +// - If full string conversion can't be performed, 0 is returned. +// - If the converted value falls out of range of corresponding return type, a +// range error occurs. In this case value MAX(T)/MIN(T) is returned. +template inline bool StringToNumber(const char *s, T *val) +{ + // Assert on `unsigned long` and `signed long` on LP64. + // If it is necessary, it could be solved with flatbuffers::enable_if. + static_assert(sizeof(T) < sizeof(int64_t), "unexpected type T"); + FLATBUFFERS_ASSERT(s && val); + int64_t i64; + // The errno check isn't needed, will return MAX/MIN on overflow. + if (StringToIntegerImpl(&i64, s, 0, false)) + { + const int64_t max = (flatbuffers::numeric_limits::max)(); + const int64_t min = flatbuffers::numeric_limits::lowest(); + if (i64 > max) + { + *val = static_cast(max); + return false; + } + if (i64 < min) + { + // For unsigned types return max to distinguish from + // "no conversion can be performed" when 0 is returned. + *val = static_cast(flatbuffers::is_unsigned::value ? max : min); + return false; + } + *val = static_cast(i64); + return true; + } + *val = 0; + return false; +} + +template <> inline bool StringToNumber(const char *str, int64_t *val) +{ + return StringToIntegerImpl(val, str); +} + +template <> inline bool StringToNumber(const char *str, uint64_t *val) +{ + if (!StringToIntegerImpl(val, str)) + return false; + // The strtoull accepts negative numbers: + // If the minus sign was part of the input sequence, the numeric value + // calculated from the sequence of digits is negated as if by unary minus + // in the result type, which applies unsigned integer wraparound rules. + // Fix this behaviour (except -0). + if (*val) + { + auto s = str; + while (*s && !is_digit(*s)) + s++; + s = (s > str) ? (s - 1) : s; // step back to one symbol + if (*s == '-') + { + // For unsigned types return the max to distinguish from + // "no conversion can be performed". + *val = (flatbuffers::numeric_limits::max)(); + return false; + } + } + return true; +} + +template <> inline bool StringToNumber(const char *s, float *val) +{ + return StringToFloatImpl(val, s); +} + +template <> inline bool StringToNumber(const char *s, double *val) +{ + return StringToFloatImpl(val, s); +} + +inline int64_t StringToInt(const char *s, int base = 10) +{ + int64_t val; + return StringToIntegerImpl(&val, s, base) ? val : 0; +} + +inline uint64_t StringToUInt(const char *s, int base = 10) +{ + uint64_t val; + return StringToIntegerImpl(&val, s, base) ? val : 0; +} + +typedef bool (*LoadFileFunction)(const char *filename, bool binary, std::string *dest); +typedef bool (*FileExistsFunction)(const char *filename); + +LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function); + +FileExistsFunction SetFileExistsFunction(FileExistsFunction file_exists_function); + +// Check if file "name" exists. +bool FileExists(const char *name); + +// Check if "name" exists and it is also a directory. +bool DirExists(const char *name); + +// Load file "name" into "buf" returning true if successful +// false otherwise. If "binary" is false data is read +// using ifstream's text mode, otherwise data is read with +// no transcoding. +bool LoadFile(const char *name, bool binary, std::string *buf); + +// Save data "buf" of length "len" bytes into a file +// "name" returning true if successful, false otherwise. +// If "binary" is false data is written using ifstream's +// text mode, otherwise data is written with no +// transcoding. +bool SaveFile(const char *name, const char *buf, size_t len, bool binary); + +// Save data "buf" into file "name" returning true if +// successful, false otherwise. If "binary" is false +// data is written using ifstream's text mode, otherwise +// data is written with no transcoding. +inline bool SaveFile(const char *name, const std::string &buf, bool binary) +{ + return SaveFile(name, buf.c_str(), buf.size(), binary); +} + +// Functionality for minimalistic portable path handling. + +// The functions below behave correctly regardless of whether posix ('/') or +// Windows ('/' or '\\') separators are used. + +// Any new separators inserted are always posix. +FLATBUFFERS_CONSTEXPR char kPathSeparator = '/'; + +// Returns the path with the extension, if any, removed. +std::string StripExtension(const std::string &filepath); + +// Returns the extension, if any. +std::string GetExtension(const std::string &filepath); + +// Return the last component of the path, after the last separator. +std::string StripPath(const std::string &filepath); + +// Strip the last component of the path + separator. +std::string StripFileName(const std::string &filepath); + +// Concatenates a path with a filename, regardless of whether the path +// ends in a separator or not. +std::string ConCatPathFileName(const std::string &path, const std::string &filename); + +// Replaces any '\\' separators with '/' +std::string PosixPath(const char *path); + +// This function ensure a directory exists, by recursively +// creating dirs for any parts of the path that don't exist yet. +void EnsureDirExists(const std::string &filepath); + +// Obtains the absolute path from any other path. +// Returns the input path if the absolute path couldn't be resolved. +std::string AbsolutePath(const std::string &filepath); + +// To and from UTF-8 unicode conversion functions + +// Convert a unicode code point into a UTF-8 representation by appending it +// to a string. Returns the number of bytes generated. +inline int ToUTF8(uint32_t ucc, std::string *out) +{ + FLATBUFFERS_ASSERT(!(ucc & 0x80000000)); // Top bit can't be set. + // 6 possible encodings: http://en.wikipedia.org/wiki/UTF-8 + for (int i = 0; i < 6; i++) + { + // Max bits this encoding can represent. + uint32_t max_bits = 6 + i * 5 + static_cast(!i); + if (ucc < (1u << max_bits)) + { // does it fit? + // Remaining bits not encoded in the first byte, store 6 bits each + uint32_t remain_bits = i * 6; + // Store first byte: + (*out) += static_cast((0xFE << (max_bits - remain_bits)) | (ucc >> remain_bits)); + // Store remaining bytes: + for (int j = i - 1; j >= 0; j--) + { + (*out) += static_cast(((ucc >> (j * 6)) & 0x3F) | 0x80); + } + return i + 1; // Return the number of bytes added. + } + } + FLATBUFFERS_ASSERT(0); // Impossible to arrive here. + return -1; +} + +// Converts whatever prefix of the incoming string corresponds to a valid +// UTF-8 sequence into a unicode code. The incoming pointer will have been +// advanced past all bytes parsed. +// returns -1 upon corrupt UTF-8 encoding (ignore the incoming pointer in +// this case). +inline int FromUTF8(const char **in) +{ + int len = 0; + // Count leading 1 bits. + for (int mask = 0x80; mask >= 0x04; mask >>= 1) + { + if (**in & mask) + { + len++; + } + else + { + break; + } + } + if ((static_cast(**in) << len) & 0x80) + return -1; // Bit after leading 1's must be 0. + if (!len) + return *(*in)++; + // UTF-8 encoded values with a length are between 2 and 4 bytes. + if (len < 2 || len > 4) + { + return -1; + } + // Grab initial bits of the code. + int ucc = *(*in)++ & ((1 << (7 - len)) - 1); + for (int i = 0; i < len - 1; i++) + { + if ((**in & 0xC0) != 0x80) + return -1; // Upper bits must 1 0. + ucc <<= 6; + ucc |= *(*in)++ & 0x3F; // Grab 6 more bits of the code. + } + // UTF-8 cannot encode values between 0xD800 and 0xDFFF (reserved for + // UTF-16 surrogate pairs). + if (ucc >= 0xD800 && ucc <= 0xDFFF) + { + return -1; + } + // UTF-8 must represent code points in their shortest possible encoding. + switch (len) + { + case 2: + // Two bytes of UTF-8 can represent code points from U+0080 to U+07FF. + if (ucc < 0x0080 || ucc > 0x07FF) + { + return -1; + } + break; + case 3: + // Three bytes of UTF-8 can represent code points from U+0800 to U+FFFF. + if (ucc < 0x0800 || ucc > 0xFFFF) + { + return -1; + } + break; + case 4: + // Four bytes of UTF-8 can represent code points from U+10000 to U+10FFFF. + if (ucc < 0x10000 || ucc > 0x10FFFF) + { + return -1; + } + break; + } + return ucc; +} + +#ifndef FLATBUFFERS_PREFER_PRINTF +// Wraps a string to a maximum length, inserting new lines where necessary. Any +// existing whitespace will be collapsed down to a single space. A prefix or +// suffix can be provided, which will be inserted before or after a wrapped +// line, respectively. +inline std::string WordWrap(const std::string in, size_t max_length, + const std::string wrapped_line_prefix, + const std::string wrapped_line_suffix) +{ + std::istringstream in_stream(in); + std::string wrapped, line, word; + + in_stream >> word; + line = word; + + while (in_stream >> word) + { + if ((line.length() + 1 + word.length() + wrapped_line_suffix.length()) < max_length) + { + line += " " + word; + } + else + { + wrapped += line + wrapped_line_suffix + "\n"; + line = wrapped_line_prefix + word; + } + } + wrapped += line; + + return wrapped; +} +#endif // !FLATBUFFERS_PREFER_PRINTF + +inline bool EscapeString(const char *s, size_t length, std::string *_text, bool allow_non_utf8, + bool natural_utf8) +{ + std::string &text = *_text; + text += "\""; + for (uoffset_t i = 0; i < length; i++) + { + char c = s[i]; + switch (c) + { + case '\n': + text += "\\n"; + break; + case '\t': + text += "\\t"; + break; + case '\r': + text += "\\r"; + break; + case '\b': + text += "\\b"; + break; + case '\f': + text += "\\f"; + break; + case '\"': + text += "\\\""; + break; + case '\\': + text += "\\\\"; + break; + default: + if (c >= ' ' && c <= '~') + { + text += c; + } + else + { + // Not printable ASCII data. Let's see if it's valid UTF-8 first: + const char *utf8 = s + i; + int ucc = FromUTF8(&utf8); + if (ucc < 0) + { + if (allow_non_utf8) + { + text += "\\x"; + text += IntToStringHex(static_cast(c), 2); + } + else + { + // There are two cases here: + // + // 1) We reached here by parsing an IDL file. In that case, + // we previously checked for non-UTF-8, so we shouldn't reach + // here. + // + // 2) We reached here by someone calling GenerateText() + // on a previously-serialized flatbuffer. The data might have + // non-UTF-8 Strings, or might be corrupt. + // + // In both cases, we have to give up and inform the caller + // they have no JSON. + return false; + } + } + else + { + if (natural_utf8) + { + // utf8 points to past all utf-8 bytes parsed + text.append(s + i, static_cast(utf8 - s - i)); + } + else if (ucc <= 0xFFFF) + { + // Parses as Unicode within JSON's \uXXXX range, so use that. + text += "\\u"; + text += IntToStringHex(ucc, 4); + } + else if (ucc <= 0x10FFFF) + { + // Encode Unicode SMP values to a surrogate pair using two \u + // escapes. + uint32_t base = ucc - 0x10000; + auto high_surrogate = (base >> 10) + 0xD800; + auto low_surrogate = (base & 0x03FF) + 0xDC00; + text += "\\u"; + text += IntToStringHex(high_surrogate, 4); + text += "\\u"; + text += IntToStringHex(low_surrogate, 4); + } + // Skip past characters recognized. + i = static_cast(utf8 - s - 1); + } + } + break; + } + } + text += "\""; + return true; +} + +inline std::string BufferToHexText(const void *buffer, size_t buffer_size, size_t max_length, + const std::string &wrapped_line_prefix, + const std::string &wrapped_line_suffix) +{ + std::string text = wrapped_line_prefix; + size_t start_offset = 0; + const char *s = reinterpret_cast(buffer); + for (size_t i = 0; s && i < buffer_size; i++) + { + // Last iteration or do we have more? + bool have_more = i + 1 < buffer_size; + text += "0x"; + text += IntToStringHex(static_cast(s[i]), 2); + if (have_more) + { + text += ','; + } + // If we have more to process and we reached max_length + if (have_more && text.size() + wrapped_line_suffix.size() >= start_offset + max_length) + { + text += wrapped_line_suffix; + text += '\n'; + start_offset = text.size(); + text += wrapped_line_prefix; + } + } + text += wrapped_line_suffix; + return text; +} + +// Remove paired quotes in a string: "text"|'text' -> text. +std::string RemoveStringQuotes(const std::string &s); + +// Change th global C-locale to locale with name . +// Returns an actual locale name in <_value>, useful if locale_name is "" or +// null. +bool SetGlobalTestLocale(const char *locale_name, std::string *_value = nullptr); + +// Read (or test) a value of environment variable. +bool ReadEnvironmentVariable(const char *var_name, std::string *_value = nullptr); + +// MSVC specific: Send all assert reports to STDOUT to prevent CI hangs. +void SetupDefaultCRTReportMode(); + +} // namespace flatbuffers + +#endif // FLATBUFFERS_UTIL_H_ diff --git a/onert-micro/externals/gen/circle-generated/circle/schema_generated.h b/onert-micro/externals/gen/circle-generated/circle/schema_generated.h new file mode 100644 index 0000000..2531319 --- /dev/null +++ b/onert-micro/externals/gen/circle-generated/circle/schema_generated.h @@ -0,0 +1,24984 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// automatically generated by the FlatBuffers compiler, do not modify + +#ifndef FLATBUFFERS_GENERATED_SCHEMA_CIRCLE_H_ +#define FLATBUFFERS_GENERATED_SCHEMA_CIRCLE_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace circle +{ + +struct CustomQuantization; +struct CustomQuantizationBuilder; +struct CustomQuantizationT; + +struct QuantizationParameters; +struct QuantizationParametersBuilder; +struct QuantizationParametersT; + +struct Int32Vector; +struct Int32VectorBuilder; +struct Int32VectorT; + +struct Uint16Vector; +struct Uint16VectorBuilder; +struct Uint16VectorT; + +struct Uint8Vector; +struct Uint8VectorBuilder; +struct Uint8VectorT; + +struct DimensionMetadata; +struct DimensionMetadataBuilder; +struct DimensionMetadataT; + +struct SparsityParameters; +struct SparsityParametersBuilder; +struct SparsityParametersT; + +struct Tensor; +struct TensorBuilder; +struct TensorT; + +struct Conv2DOptions; +struct Conv2DOptionsBuilder; +struct Conv2DOptionsT; + +struct Conv3DOptions; +struct Conv3DOptionsBuilder; +struct Conv3DOptionsT; + +struct Pool2DOptions; +struct Pool2DOptionsBuilder; +struct Pool2DOptionsT; + +struct DepthwiseConv2DOptions; +struct DepthwiseConv2DOptionsBuilder; +struct DepthwiseConv2DOptionsT; + +struct ConcatEmbeddingsOptions; +struct ConcatEmbeddingsOptionsBuilder; +struct ConcatEmbeddingsOptionsT; + +struct LSHProjectionOptions; +struct LSHProjectionOptionsBuilder; +struct LSHProjectionOptionsT; + +struct SVDFOptions; +struct SVDFOptionsBuilder; +struct SVDFOptionsT; + +struct RNNOptions; +struct RNNOptionsBuilder; +struct RNNOptionsT; + +struct SequenceRNNOptions; +struct SequenceRNNOptionsBuilder; +struct SequenceRNNOptionsT; + +struct BidirectionalSequenceRNNOptions; +struct BidirectionalSequenceRNNOptionsBuilder; +struct BidirectionalSequenceRNNOptionsT; + +struct FullyConnectedOptions; +struct FullyConnectedOptionsBuilder; +struct FullyConnectedOptionsT; + +struct SoftmaxOptions; +struct SoftmaxOptionsBuilder; +struct SoftmaxOptionsT; + +struct ConcatenationOptions; +struct ConcatenationOptionsBuilder; +struct ConcatenationOptionsT; + +struct AddOptions; +struct AddOptionsBuilder; +struct AddOptionsT; + +struct MulOptions; +struct MulOptionsBuilder; +struct MulOptionsT; + +struct L2NormOptions; +struct L2NormOptionsBuilder; +struct L2NormOptionsT; + +struct LocalResponseNormalizationOptions; +struct LocalResponseNormalizationOptionsBuilder; +struct LocalResponseNormalizationOptionsT; + +struct LSTMOptions; +struct LSTMOptionsBuilder; +struct LSTMOptionsT; + +struct UnidirectionalSequenceLSTMOptions; +struct UnidirectionalSequenceLSTMOptionsBuilder; +struct UnidirectionalSequenceLSTMOptionsT; + +struct BidirectionalSequenceLSTMOptions; +struct BidirectionalSequenceLSTMOptionsBuilder; +struct BidirectionalSequenceLSTMOptionsT; + +struct ResizeBilinearOptions; +struct ResizeBilinearOptionsBuilder; +struct ResizeBilinearOptionsT; + +struct ResizeNearestNeighborOptions; +struct ResizeNearestNeighborOptionsBuilder; +struct ResizeNearestNeighborOptionsT; + +struct CallOptions; +struct CallOptionsBuilder; +struct CallOptionsT; + +struct PadOptions; +struct PadOptionsBuilder; +struct PadOptionsT; + +struct PadV2Options; +struct PadV2OptionsBuilder; +struct PadV2OptionsT; + +struct ReshapeOptions; +struct ReshapeOptionsBuilder; +struct ReshapeOptionsT; + +struct SpaceToBatchNDOptions; +struct SpaceToBatchNDOptionsBuilder; +struct SpaceToBatchNDOptionsT; + +struct BatchToSpaceNDOptions; +struct BatchToSpaceNDOptionsBuilder; +struct BatchToSpaceNDOptionsT; + +struct SkipGramOptions; +struct SkipGramOptionsBuilder; +struct SkipGramOptionsT; + +struct SpaceToDepthOptions; +struct SpaceToDepthOptionsBuilder; +struct SpaceToDepthOptionsT; + +struct DepthToSpaceOptions; +struct DepthToSpaceOptionsBuilder; +struct DepthToSpaceOptionsT; + +struct SubOptions; +struct SubOptionsBuilder; +struct SubOptionsT; + +struct DivOptions; +struct DivOptionsBuilder; +struct DivOptionsT; + +struct TopKV2Options; +struct TopKV2OptionsBuilder; +struct TopKV2OptionsT; + +struct EmbeddingLookupSparseOptions; +struct EmbeddingLookupSparseOptionsBuilder; +struct EmbeddingLookupSparseOptionsT; + +struct GatherOptions; +struct GatherOptionsBuilder; +struct GatherOptionsT; + +struct TransposeOptions; +struct TransposeOptionsBuilder; +struct TransposeOptionsT; + +struct ExpOptions; +struct ExpOptionsBuilder; +struct ExpOptionsT; + +struct CosOptions; +struct CosOptionsBuilder; +struct CosOptionsT; + +struct ReducerOptions; +struct ReducerOptionsBuilder; +struct ReducerOptionsT; + +struct SqueezeOptions; +struct SqueezeOptionsBuilder; +struct SqueezeOptionsT; + +struct SplitOptions; +struct SplitOptionsBuilder; +struct SplitOptionsT; + +struct SplitVOptions; +struct SplitVOptionsBuilder; +struct SplitVOptionsT; + +struct StridedSliceOptions; +struct StridedSliceOptionsBuilder; +struct StridedSliceOptionsT; + +struct LogSoftmaxOptions; +struct LogSoftmaxOptionsBuilder; +struct LogSoftmaxOptionsT; + +struct CastOptions; +struct CastOptionsBuilder; +struct CastOptionsT; + +struct DequantizeOptions; +struct DequantizeOptionsBuilder; +struct DequantizeOptionsT; + +struct MaximumMinimumOptions; +struct MaximumMinimumOptionsBuilder; +struct MaximumMinimumOptionsT; + +struct TileOptions; +struct TileOptionsBuilder; +struct TileOptionsT; + +struct ArgMaxOptions; +struct ArgMaxOptionsBuilder; +struct ArgMaxOptionsT; + +struct ArgMinOptions; +struct ArgMinOptionsBuilder; +struct ArgMinOptionsT; + +struct GreaterOptions; +struct GreaterOptionsBuilder; +struct GreaterOptionsT; + +struct GreaterEqualOptions; +struct GreaterEqualOptionsBuilder; +struct GreaterEqualOptionsT; + +struct LessOptions; +struct LessOptionsBuilder; +struct LessOptionsT; + +struct LessEqualOptions; +struct LessEqualOptionsBuilder; +struct LessEqualOptionsT; + +struct NegOptions; +struct NegOptionsBuilder; +struct NegOptionsT; + +struct SelectOptions; +struct SelectOptionsBuilder; +struct SelectOptionsT; + +struct SliceOptions; +struct SliceOptionsBuilder; +struct SliceOptionsT; + +struct TransposeConvOptions; +struct TransposeConvOptionsBuilder; +struct TransposeConvOptionsT; + +struct ExpandDimsOptions; +struct ExpandDimsOptionsBuilder; +struct ExpandDimsOptionsT; + +struct SparseToDenseOptions; +struct SparseToDenseOptionsBuilder; +struct SparseToDenseOptionsT; + +struct EqualOptions; +struct EqualOptionsBuilder; +struct EqualOptionsT; + +struct NotEqualOptions; +struct NotEqualOptionsBuilder; +struct NotEqualOptionsT; + +struct ShapeOptions; +struct ShapeOptionsBuilder; +struct ShapeOptionsT; + +struct RankOptions; +struct RankOptionsBuilder; +struct RankOptionsT; + +struct PowOptions; +struct PowOptionsBuilder; +struct PowOptionsT; + +struct FakeQuantOptions; +struct FakeQuantOptionsBuilder; +struct FakeQuantOptionsT; + +struct PackOptions; +struct PackOptionsBuilder; +struct PackOptionsT; + +struct LogicalOrOptions; +struct LogicalOrOptionsBuilder; +struct LogicalOrOptionsT; + +struct OneHotOptions; +struct OneHotOptionsBuilder; +struct OneHotOptionsT; + +struct AbsOptions; +struct AbsOptionsBuilder; +struct AbsOptionsT; + +struct HardSwishOptions; +struct HardSwishOptionsBuilder; +struct HardSwishOptionsT; + +struct LogicalAndOptions; +struct LogicalAndOptionsBuilder; +struct LogicalAndOptionsT; + +struct LogicalNotOptions; +struct LogicalNotOptionsBuilder; +struct LogicalNotOptionsT; + +struct UnpackOptions; +struct UnpackOptionsBuilder; +struct UnpackOptionsT; + +struct FloorDivOptions; +struct FloorDivOptionsBuilder; +struct FloorDivOptionsT; + +struct SquareOptions; +struct SquareOptionsBuilder; +struct SquareOptionsT; + +struct ZerosLikeOptions; +struct ZerosLikeOptionsBuilder; +struct ZerosLikeOptionsT; + +struct FillOptions; +struct FillOptionsBuilder; +struct FillOptionsT; + +struct FloorModOptions; +struct FloorModOptionsBuilder; +struct FloorModOptionsT; + +struct RangeOptions; +struct RangeOptionsBuilder; +struct RangeOptionsT; + +struct LeakyReluOptions; +struct LeakyReluOptionsBuilder; +struct LeakyReluOptionsT; + +struct SquaredDifferenceOptions; +struct SquaredDifferenceOptionsBuilder; +struct SquaredDifferenceOptionsT; + +struct MirrorPadOptions; +struct MirrorPadOptionsBuilder; +struct MirrorPadOptionsT; + +struct UniqueOptions; +struct UniqueOptionsBuilder; +struct UniqueOptionsT; + +struct ReverseV2Options; +struct ReverseV2OptionsBuilder; +struct ReverseV2OptionsT; + +struct AddNOptions; +struct AddNOptionsBuilder; +struct AddNOptionsT; + +struct GatherNdOptions; +struct GatherNdOptionsBuilder; +struct GatherNdOptionsT; + +struct WhereOptions; +struct WhereOptionsBuilder; +struct WhereOptionsT; + +struct ReverseSequenceOptions; +struct ReverseSequenceOptionsBuilder; +struct ReverseSequenceOptionsT; + +struct MatrixDiagOptions; +struct MatrixDiagOptionsBuilder; +struct MatrixDiagOptionsT; + +struct QuantizeOptions; +struct QuantizeOptionsBuilder; +struct QuantizeOptionsT; + +struct MatrixSetDiagOptions; +struct MatrixSetDiagOptionsBuilder; +struct MatrixSetDiagOptionsT; + +struct IfOptions; +struct IfOptionsBuilder; +struct IfOptionsT; + +struct CallOnceOptions; +struct CallOnceOptionsBuilder; +struct CallOnceOptionsT; + +struct WhileOptions; +struct WhileOptionsBuilder; +struct WhileOptionsT; + +struct NonMaxSuppressionV4Options; +struct NonMaxSuppressionV4OptionsBuilder; +struct NonMaxSuppressionV4OptionsT; + +struct NonMaxSuppressionV5Options; +struct NonMaxSuppressionV5OptionsBuilder; +struct NonMaxSuppressionV5OptionsT; + +struct ScatterNdOptions; +struct ScatterNdOptionsBuilder; +struct ScatterNdOptionsT; + +struct SelectV2Options; +struct SelectV2OptionsBuilder; +struct SelectV2OptionsT; + +struct DensifyOptions; +struct DensifyOptionsBuilder; +struct DensifyOptionsT; + +struct SegmentSumOptions; +struct SegmentSumOptionsBuilder; +struct SegmentSumOptionsT; + +struct BatchMatMulOptions; +struct BatchMatMulOptionsBuilder; +struct BatchMatMulOptionsT; + +struct CumsumOptions; +struct CumsumOptionsBuilder; +struct CumsumOptionsT; + +struct BroadcastToOptions; +struct BroadcastToOptionsBuilder; +struct BroadcastToOptionsT; + +struct Rfft2dOptions; +struct Rfft2dOptionsBuilder; +struct Rfft2dOptionsT; + +struct HashtableOptions; +struct HashtableOptionsBuilder; +struct HashtableOptionsT; + +struct HashtableFindOptions; +struct HashtableFindOptionsBuilder; +struct HashtableFindOptionsT; + +struct HashtableImportOptions; +struct HashtableImportOptionsBuilder; +struct HashtableImportOptionsT; + +struct HashtableSizeOptions; +struct HashtableSizeOptionsBuilder; +struct HashtableSizeOptionsT; + +struct VarHandleOptions; +struct VarHandleOptionsBuilder; +struct VarHandleOptionsT; + +struct ReadVariableOptions; +struct ReadVariableOptionsBuilder; +struct ReadVariableOptionsT; + +struct AssignVariableOptions; +struct AssignVariableOptionsBuilder; +struct AssignVariableOptionsT; + +struct RandomOptions; +struct RandomOptionsBuilder; +struct RandomOptionsT; + +struct BCQGatherOptions; +struct BCQGatherOptionsBuilder; +struct BCQGatherOptionsT; + +struct BCQFullyConnectedOptions; +struct BCQFullyConnectedOptionsBuilder; +struct BCQFullyConnectedOptionsT; + +struct InstanceNormOptions; +struct InstanceNormOptionsBuilder; +struct InstanceNormOptionsT; + +struct OperatorCode; +struct OperatorCodeBuilder; +struct OperatorCodeT; + +struct Operator; +struct OperatorBuilder; +struct OperatorT; + +struct SubGraph; +struct SubGraphBuilder; +struct SubGraphT; + +struct Buffer; +struct BufferBuilder; +struct BufferT; + +struct Metadata; +struct MetadataBuilder; +struct MetadataT; + +struct TensorMap; +struct TensorMapBuilder; +struct TensorMapT; + +struct SignatureDef; +struct SignatureDefBuilder; +struct SignatureDefT; + +struct Model; +struct ModelBuilder; +struct ModelT; + +enum TensorType : int8_t +{ + TensorType_FLOAT32 = 0, + TensorType_FLOAT16 = 1, + TensorType_INT32 = 2, + TensorType_UINT8 = 3, + TensorType_INT64 = 4, + TensorType_STRING = 5, + TensorType_BOOL = 6, + TensorType_INT16 = 7, + TensorType_COMPLEX64 = 8, + TensorType_INT8 = 9, + TensorType_FLOAT64 = 10, + TensorType_COMPLEX128 = 11, + TensorType_UINT64 = 12, + TensorType_RESOURCE = 13, + TensorType_VARIANT = 14, + TensorType_UINT32 = 15, + TensorType_MIN = TensorType_FLOAT32, + TensorType_MAX = TensorType_UINT32 +}; + +inline const TensorType (&EnumValuesTensorType())[16] +{ + static const TensorType values[] = { + TensorType_FLOAT32, TensorType_FLOAT16, TensorType_INT32, TensorType_UINT8, + TensorType_INT64, TensorType_STRING, TensorType_BOOL, TensorType_INT16, + TensorType_COMPLEX64, TensorType_INT8, TensorType_FLOAT64, TensorType_COMPLEX128, + TensorType_UINT64, TensorType_RESOURCE, TensorType_VARIANT, TensorType_UINT32}; + return values; +} + +inline const char *const *EnumNamesTensorType() +{ + static const char *const names[17] = {"FLOAT32", "FLOAT16", "INT32", "UINT8", "INT64", + "STRING", "BOOL", "INT16", "COMPLEX64", "INT8", + "FLOAT64", "COMPLEX128", "UINT64", "RESOURCE", "VARIANT", + "UINT32", nullptr}; + return names; +} + +inline const char *EnumNameTensorType(TensorType e) +{ + if (flatbuffers::IsOutRange(e, TensorType_FLOAT32, TensorType_UINT32)) + return ""; + const size_t index = static_cast(e); + return EnumNamesTensorType()[index]; +} + +enum QuantizationDetails : uint8_t +{ + QuantizationDetails_NONE = 0, + QuantizationDetails_CustomQuantization = 1, + QuantizationDetails_MIN = QuantizationDetails_NONE, + QuantizationDetails_MAX = QuantizationDetails_CustomQuantization +}; + +inline const QuantizationDetails (&EnumValuesQuantizationDetails())[2] +{ + static const QuantizationDetails values[] = {QuantizationDetails_NONE, + QuantizationDetails_CustomQuantization}; + return values; +} + +inline const char *const *EnumNamesQuantizationDetails() +{ + static const char *const names[3] = {"NONE", "CustomQuantization", nullptr}; + return names; +} + +inline const char *EnumNameQuantizationDetails(QuantizationDetails e) +{ + if (flatbuffers::IsOutRange(e, QuantizationDetails_NONE, QuantizationDetails_CustomQuantization)) + return ""; + const size_t index = static_cast(e); + return EnumNamesQuantizationDetails()[index]; +} + +template struct QuantizationDetailsTraits +{ + static const QuantizationDetails enum_value = QuantizationDetails_NONE; +}; + +template <> struct QuantizationDetailsTraits +{ + static const QuantizationDetails enum_value = QuantizationDetails_CustomQuantization; +}; + +struct QuantizationDetailsUnion +{ + QuantizationDetails type; + void *value; + + QuantizationDetailsUnion() : type(QuantizationDetails_NONE), value(nullptr) {} + QuantizationDetailsUnion(QuantizationDetailsUnion &&u) FLATBUFFERS_NOEXCEPT + : type(QuantizationDetails_NONE), + value(nullptr) + { + std::swap(type, u.type); + std::swap(value, u.value); + } + QuantizationDetailsUnion(const QuantizationDetailsUnion &); + QuantizationDetailsUnion &operator=(const QuantizationDetailsUnion &u) + { + QuantizationDetailsUnion t(u); + std::swap(type, t.type); + std::swap(value, t.value); + return *this; + } + QuantizationDetailsUnion &operator=(QuantizationDetailsUnion &&u) FLATBUFFERS_NOEXCEPT + { + std::swap(type, u.type); + std::swap(value, u.value); + return *this; + } + ~QuantizationDetailsUnion() { Reset(); } + + void Reset(); + +#ifndef FLATBUFFERS_CPP98_STL + template void Set(T &&val) + { + using RT = typename std::remove_reference::type; + Reset(); + type = QuantizationDetailsTraits::enum_value; + if (type != QuantizationDetails_NONE) + { + value = new RT(std::forward(val)); + } + } +#endif // FLATBUFFERS_CPP98_STL + + static void *UnPack(const void *obj, QuantizationDetails type, + const flatbuffers::resolver_function_t *resolver); + flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher = nullptr) const; + + circle::CustomQuantizationT *AsCustomQuantization() + { + return type == QuantizationDetails_CustomQuantization + ? reinterpret_cast(value) + : nullptr; + } + const circle::CustomQuantizationT *AsCustomQuantization() const + { + return type == QuantizationDetails_CustomQuantization + ? reinterpret_cast(value) + : nullptr; + } +}; + +bool VerifyQuantizationDetails(flatbuffers::Verifier &verifier, const void *obj, + QuantizationDetails type); +bool VerifyQuantizationDetailsVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types); + +enum DimensionType : int8_t +{ + DimensionType_DENSE = 0, + DimensionType_SPARSE_CSR = 1, + DimensionType_MIN = DimensionType_DENSE, + DimensionType_MAX = DimensionType_SPARSE_CSR +}; + +inline const DimensionType (&EnumValuesDimensionType())[2] +{ + static const DimensionType values[] = {DimensionType_DENSE, DimensionType_SPARSE_CSR}; + return values; +} + +inline const char *const *EnumNamesDimensionType() +{ + static const char *const names[3] = {"DENSE", "SPARSE_CSR", nullptr}; + return names; +} + +inline const char *EnumNameDimensionType(DimensionType e) +{ + if (flatbuffers::IsOutRange(e, DimensionType_DENSE, DimensionType_SPARSE_CSR)) + return ""; + const size_t index = static_cast(e); + return EnumNamesDimensionType()[index]; +} + +enum SparseIndexVector : uint8_t +{ + SparseIndexVector_NONE = 0, + SparseIndexVector_Int32Vector = 1, + SparseIndexVector_Uint16Vector = 2, + SparseIndexVector_Uint8Vector = 3, + SparseIndexVector_MIN = SparseIndexVector_NONE, + SparseIndexVector_MAX = SparseIndexVector_Uint8Vector +}; + +inline const SparseIndexVector (&EnumValuesSparseIndexVector())[4] +{ + static const SparseIndexVector values[] = {SparseIndexVector_NONE, SparseIndexVector_Int32Vector, + SparseIndexVector_Uint16Vector, + SparseIndexVector_Uint8Vector}; + return values; +} + +inline const char *const *EnumNamesSparseIndexVector() +{ + static const char *const names[5] = {"NONE", "Int32Vector", "Uint16Vector", "Uint8Vector", + nullptr}; + return names; +} + +inline const char *EnumNameSparseIndexVector(SparseIndexVector e) +{ + if (flatbuffers::IsOutRange(e, SparseIndexVector_NONE, SparseIndexVector_Uint8Vector)) + return ""; + const size_t index = static_cast(e); + return EnumNamesSparseIndexVector()[index]; +} + +template struct SparseIndexVectorTraits +{ + static const SparseIndexVector enum_value = SparseIndexVector_NONE; +}; + +template <> struct SparseIndexVectorTraits +{ + static const SparseIndexVector enum_value = SparseIndexVector_Int32Vector; +}; + +template <> struct SparseIndexVectorTraits +{ + static const SparseIndexVector enum_value = SparseIndexVector_Uint16Vector; +}; + +template <> struct SparseIndexVectorTraits +{ + static const SparseIndexVector enum_value = SparseIndexVector_Uint8Vector; +}; + +struct SparseIndexVectorUnion +{ + SparseIndexVector type; + void *value; + + SparseIndexVectorUnion() : type(SparseIndexVector_NONE), value(nullptr) {} + SparseIndexVectorUnion(SparseIndexVectorUnion &&u) FLATBUFFERS_NOEXCEPT + : type(SparseIndexVector_NONE), + value(nullptr) + { + std::swap(type, u.type); + std::swap(value, u.value); + } + SparseIndexVectorUnion(const SparseIndexVectorUnion &); + SparseIndexVectorUnion &operator=(const SparseIndexVectorUnion &u) + { + SparseIndexVectorUnion t(u); + std::swap(type, t.type); + std::swap(value, t.value); + return *this; + } + SparseIndexVectorUnion &operator=(SparseIndexVectorUnion &&u) FLATBUFFERS_NOEXCEPT + { + std::swap(type, u.type); + std::swap(value, u.value); + return *this; + } + ~SparseIndexVectorUnion() { Reset(); } + + void Reset(); + +#ifndef FLATBUFFERS_CPP98_STL + template void Set(T &&val) + { + using RT = typename std::remove_reference::type; + Reset(); + type = SparseIndexVectorTraits::enum_value; + if (type != SparseIndexVector_NONE) + { + value = new RT(std::forward(val)); + } + } +#endif // FLATBUFFERS_CPP98_STL + + static void *UnPack(const void *obj, SparseIndexVector type, + const flatbuffers::resolver_function_t *resolver); + flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher = nullptr) const; + + circle::Int32VectorT *AsInt32Vector() + { + return type == SparseIndexVector_Int32Vector ? reinterpret_cast(value) + : nullptr; + } + const circle::Int32VectorT *AsInt32Vector() const + { + return type == SparseIndexVector_Int32Vector + ? reinterpret_cast(value) + : nullptr; + } + circle::Uint16VectorT *AsUint16Vector() + { + return type == SparseIndexVector_Uint16Vector ? reinterpret_cast(value) + : nullptr; + } + const circle::Uint16VectorT *AsUint16Vector() const + { + return type == SparseIndexVector_Uint16Vector + ? reinterpret_cast(value) + : nullptr; + } + circle::Uint8VectorT *AsUint8Vector() + { + return type == SparseIndexVector_Uint8Vector ? reinterpret_cast(value) + : nullptr; + } + const circle::Uint8VectorT *AsUint8Vector() const + { + return type == SparseIndexVector_Uint8Vector + ? reinterpret_cast(value) + : nullptr; + } +}; + +bool VerifySparseIndexVector(flatbuffers::Verifier &verifier, const void *obj, + SparseIndexVector type); +bool VerifySparseIndexVectorVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types); + +enum BuiltinOperator : int32_t +{ + BuiltinOperator_BCQ_GATHER = -4, + BuiltinOperator_BCQ_FULLY_CONNECTED = -3, + BuiltinOperator_INSTANCE_NORM = -2, + BuiltinOperator_ADD = 0, + BuiltinOperator_AVERAGE_POOL_2D = 1, + BuiltinOperator_CONCATENATION = 2, + BuiltinOperator_CONV_2D = 3, + BuiltinOperator_DEPTHWISE_CONV_2D = 4, + BuiltinOperator_DEPTH_TO_SPACE = 5, + BuiltinOperator_DEQUANTIZE = 6, + BuiltinOperator_EMBEDDING_LOOKUP = 7, + BuiltinOperator_FLOOR = 8, + BuiltinOperator_FULLY_CONNECTED = 9, + BuiltinOperator_HASHTABLE_LOOKUP = 10, + BuiltinOperator_L2_NORMALIZATION = 11, + BuiltinOperator_L2_POOL_2D = 12, + BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION = 13, + BuiltinOperator_LOGISTIC = 14, + BuiltinOperator_LSH_PROJECTION = 15, + BuiltinOperator_LSTM = 16, + BuiltinOperator_MAX_POOL_2D = 17, + BuiltinOperator_MUL = 18, + BuiltinOperator_RELU = 19, + BuiltinOperator_RELU_N1_TO_1 = 20, + BuiltinOperator_RELU6 = 21, + BuiltinOperator_RESHAPE = 22, + BuiltinOperator_RESIZE_BILINEAR = 23, + BuiltinOperator_RNN = 24, + BuiltinOperator_SOFTMAX = 25, + BuiltinOperator_SPACE_TO_DEPTH = 26, + BuiltinOperator_SVDF = 27, + BuiltinOperator_TANH = 28, + BuiltinOperator_CONCAT_EMBEDDINGS = 29, + BuiltinOperator_SKIP_GRAM = 30, + BuiltinOperator_CALL = 31, + BuiltinOperator_CUSTOM = 32, + BuiltinOperator_EMBEDDING_LOOKUP_SPARSE = 33, + BuiltinOperator_PAD = 34, + BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35, + BuiltinOperator_GATHER = 36, + BuiltinOperator_BATCH_TO_SPACE_ND = 37, + BuiltinOperator_SPACE_TO_BATCH_ND = 38, + BuiltinOperator_TRANSPOSE = 39, + BuiltinOperator_MEAN = 40, + BuiltinOperator_SUB = 41, + BuiltinOperator_DIV = 42, + BuiltinOperator_SQUEEZE = 43, + BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM = 44, + BuiltinOperator_STRIDED_SLICE = 45, + BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46, + BuiltinOperator_EXP = 47, + BuiltinOperator_TOPK_V2 = 48, + BuiltinOperator_SPLIT = 49, + BuiltinOperator_LOG_SOFTMAX = 50, + BuiltinOperator_DELEGATE = 51, + BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52, + BuiltinOperator_CAST = 53, + BuiltinOperator_PRELU = 54, + BuiltinOperator_MAXIMUM = 55, + BuiltinOperator_ARG_MAX = 56, + BuiltinOperator_MINIMUM = 57, + BuiltinOperator_LESS = 58, + BuiltinOperator_NEG = 59, + BuiltinOperator_PADV2 = 60, + BuiltinOperator_GREATER = 61, + BuiltinOperator_GREATER_EQUAL = 62, + BuiltinOperator_LESS_EQUAL = 63, + BuiltinOperator_SELECT = 64, + BuiltinOperator_SLICE = 65, + BuiltinOperator_SIN = 66, + BuiltinOperator_TRANSPOSE_CONV = 67, + BuiltinOperator_SPARSE_TO_DENSE = 68, + BuiltinOperator_TILE = 69, + BuiltinOperator_EXPAND_DIMS = 70, + BuiltinOperator_EQUAL = 71, + BuiltinOperator_NOT_EQUAL = 72, + BuiltinOperator_LOG = 73, + BuiltinOperator_SUM = 74, + BuiltinOperator_SQRT = 75, + BuiltinOperator_RSQRT = 76, + BuiltinOperator_SHAPE = 77, + BuiltinOperator_POW = 78, + BuiltinOperator_ARG_MIN = 79, + BuiltinOperator_FAKE_QUANT = 80, + BuiltinOperator_REDUCE_PROD = 81, + BuiltinOperator_REDUCE_MAX = 82, + BuiltinOperator_PACK = 83, + BuiltinOperator_LOGICAL_OR = 84, + BuiltinOperator_ONE_HOT = 85, + BuiltinOperator_LOGICAL_AND = 86, + BuiltinOperator_LOGICAL_NOT = 87, + BuiltinOperator_UNPACK = 88, + BuiltinOperator_REDUCE_MIN = 89, + BuiltinOperator_FLOOR_DIV = 90, + BuiltinOperator_REDUCE_ANY = 91, + BuiltinOperator_SQUARE = 92, + BuiltinOperator_ZEROS_LIKE = 93, + BuiltinOperator_FILL = 94, + BuiltinOperator_FLOOR_MOD = 95, + BuiltinOperator_RANGE = 96, + BuiltinOperator_RESIZE_NEAREST_NEIGHBOR = 97, + BuiltinOperator_LEAKY_RELU = 98, + BuiltinOperator_SQUARED_DIFFERENCE = 99, + BuiltinOperator_MIRROR_PAD = 100, + BuiltinOperator_ABS = 101, + BuiltinOperator_SPLIT_V = 102, + BuiltinOperator_UNIQUE = 103, + BuiltinOperator_CEIL = 104, + BuiltinOperator_REVERSE_V2 = 105, + BuiltinOperator_ADD_N = 106, + BuiltinOperator_GATHER_ND = 107, + BuiltinOperator_COS = 108, + BuiltinOperator_WHERE = 109, + BuiltinOperator_RANK = 110, + BuiltinOperator_ELU = 111, + BuiltinOperator_REVERSE_SEQUENCE = 112, + BuiltinOperator_MATRIX_DIAG = 113, + BuiltinOperator_QUANTIZE = 114, + BuiltinOperator_MATRIX_SET_DIAG = 115, + BuiltinOperator_ROUND = 116, + BuiltinOperator_HARD_SWISH = 117, + BuiltinOperator_IF = 118, + BuiltinOperator_WHILE = 119, + BuiltinOperator_NON_MAX_SUPPRESSION_V4 = 120, + BuiltinOperator_NON_MAX_SUPPRESSION_V5 = 121, + BuiltinOperator_SCATTER_ND = 122, + BuiltinOperator_SELECT_V2 = 123, + BuiltinOperator_DENSIFY = 124, + BuiltinOperator_SEGMENT_SUM = 125, + BuiltinOperator_BATCH_MATMUL = 126, + BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES = 127, + BuiltinOperator_CUMSUM = 128, + BuiltinOperator_CALL_ONCE = 129, + BuiltinOperator_BROADCAST_TO = 130, + BuiltinOperator_RFFT2D = 131, + BuiltinOperator_CONV_3D = 132, + BuiltinOperator_IMAG = 133, + BuiltinOperator_REAL = 134, + BuiltinOperator_COMPLEX_ABS = 135, + BuiltinOperator_HASHTABLE = 136, + BuiltinOperator_HASHTABLE_FIND = 137, + BuiltinOperator_HASHTABLE_IMPORT = 138, + BuiltinOperator_HASHTABLE_SIZE = 139, + BuiltinOperator_REDUCE_ALL = 140, + BuiltinOperator_CONV_3D_TRANSPOSE = 141, + BuiltinOperator_VAR_HANDLE = 142, + BuiltinOperator_READ_VARIABLE = 143, + BuiltinOperator_ASSIGN_VARIABLE = 144, + BuiltinOperator_BROADCAST_ARGS = 145, + BuiltinOperator_RANDOM_STANDARD_NORMAL = 146, + BuiltinOperator_MIN = BuiltinOperator_BCQ_GATHER, + BuiltinOperator_MAX = BuiltinOperator_RANDOM_STANDARD_NORMAL +}; + +inline const BuiltinOperator (&EnumValuesBuiltinOperator())[150] +{ + static const BuiltinOperator values[] = {BuiltinOperator_BCQ_GATHER, + BuiltinOperator_BCQ_FULLY_CONNECTED, + BuiltinOperator_INSTANCE_NORM, + BuiltinOperator_ADD, + BuiltinOperator_AVERAGE_POOL_2D, + BuiltinOperator_CONCATENATION, + BuiltinOperator_CONV_2D, + BuiltinOperator_DEPTHWISE_CONV_2D, + BuiltinOperator_DEPTH_TO_SPACE, + BuiltinOperator_DEQUANTIZE, + BuiltinOperator_EMBEDDING_LOOKUP, + BuiltinOperator_FLOOR, + BuiltinOperator_FULLY_CONNECTED, + BuiltinOperator_HASHTABLE_LOOKUP, + BuiltinOperator_L2_NORMALIZATION, + BuiltinOperator_L2_POOL_2D, + BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION, + BuiltinOperator_LOGISTIC, + BuiltinOperator_LSH_PROJECTION, + BuiltinOperator_LSTM, + BuiltinOperator_MAX_POOL_2D, + BuiltinOperator_MUL, + BuiltinOperator_RELU, + BuiltinOperator_RELU_N1_TO_1, + BuiltinOperator_RELU6, + BuiltinOperator_RESHAPE, + BuiltinOperator_RESIZE_BILINEAR, + BuiltinOperator_RNN, + BuiltinOperator_SOFTMAX, + BuiltinOperator_SPACE_TO_DEPTH, + BuiltinOperator_SVDF, + BuiltinOperator_TANH, + BuiltinOperator_CONCAT_EMBEDDINGS, + BuiltinOperator_SKIP_GRAM, + BuiltinOperator_CALL, + BuiltinOperator_CUSTOM, + BuiltinOperator_EMBEDDING_LOOKUP_SPARSE, + BuiltinOperator_PAD, + BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, + BuiltinOperator_GATHER, + BuiltinOperator_BATCH_TO_SPACE_ND, + BuiltinOperator_SPACE_TO_BATCH_ND, + BuiltinOperator_TRANSPOSE, + BuiltinOperator_MEAN, + BuiltinOperator_SUB, + BuiltinOperator_DIV, + BuiltinOperator_SQUEEZE, + BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, + BuiltinOperator_STRIDED_SLICE, + BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, + BuiltinOperator_EXP, + BuiltinOperator_TOPK_V2, + BuiltinOperator_SPLIT, + BuiltinOperator_LOG_SOFTMAX, + BuiltinOperator_DELEGATE, + BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, + BuiltinOperator_CAST, + BuiltinOperator_PRELU, + BuiltinOperator_MAXIMUM, + BuiltinOperator_ARG_MAX, + BuiltinOperator_MINIMUM, + BuiltinOperator_LESS, + BuiltinOperator_NEG, + BuiltinOperator_PADV2, + BuiltinOperator_GREATER, + BuiltinOperator_GREATER_EQUAL, + BuiltinOperator_LESS_EQUAL, + BuiltinOperator_SELECT, + BuiltinOperator_SLICE, + BuiltinOperator_SIN, + BuiltinOperator_TRANSPOSE_CONV, + BuiltinOperator_SPARSE_TO_DENSE, + BuiltinOperator_TILE, + BuiltinOperator_EXPAND_DIMS, + BuiltinOperator_EQUAL, + BuiltinOperator_NOT_EQUAL, + BuiltinOperator_LOG, + BuiltinOperator_SUM, + BuiltinOperator_SQRT, + BuiltinOperator_RSQRT, + BuiltinOperator_SHAPE, + BuiltinOperator_POW, + BuiltinOperator_ARG_MIN, + BuiltinOperator_FAKE_QUANT, + BuiltinOperator_REDUCE_PROD, + BuiltinOperator_REDUCE_MAX, + BuiltinOperator_PACK, + BuiltinOperator_LOGICAL_OR, + BuiltinOperator_ONE_HOT, + BuiltinOperator_LOGICAL_AND, + BuiltinOperator_LOGICAL_NOT, + BuiltinOperator_UNPACK, + BuiltinOperator_REDUCE_MIN, + BuiltinOperator_FLOOR_DIV, + BuiltinOperator_REDUCE_ANY, + BuiltinOperator_SQUARE, + BuiltinOperator_ZEROS_LIKE, + BuiltinOperator_FILL, + BuiltinOperator_FLOOR_MOD, + BuiltinOperator_RANGE, + BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, + BuiltinOperator_LEAKY_RELU, + BuiltinOperator_SQUARED_DIFFERENCE, + BuiltinOperator_MIRROR_PAD, + BuiltinOperator_ABS, + BuiltinOperator_SPLIT_V, + BuiltinOperator_UNIQUE, + BuiltinOperator_CEIL, + BuiltinOperator_REVERSE_V2, + BuiltinOperator_ADD_N, + BuiltinOperator_GATHER_ND, + BuiltinOperator_COS, + BuiltinOperator_WHERE, + BuiltinOperator_RANK, + BuiltinOperator_ELU, + BuiltinOperator_REVERSE_SEQUENCE, + BuiltinOperator_MATRIX_DIAG, + BuiltinOperator_QUANTIZE, + BuiltinOperator_MATRIX_SET_DIAG, + BuiltinOperator_ROUND, + BuiltinOperator_HARD_SWISH, + BuiltinOperator_IF, + BuiltinOperator_WHILE, + BuiltinOperator_NON_MAX_SUPPRESSION_V4, + BuiltinOperator_NON_MAX_SUPPRESSION_V5, + BuiltinOperator_SCATTER_ND, + BuiltinOperator_SELECT_V2, + BuiltinOperator_DENSIFY, + BuiltinOperator_SEGMENT_SUM, + BuiltinOperator_BATCH_MATMUL, + BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES, + BuiltinOperator_CUMSUM, + BuiltinOperator_CALL_ONCE, + BuiltinOperator_BROADCAST_TO, + BuiltinOperator_RFFT2D, + BuiltinOperator_CONV_3D, + BuiltinOperator_IMAG, + BuiltinOperator_REAL, + BuiltinOperator_COMPLEX_ABS, + BuiltinOperator_HASHTABLE, + BuiltinOperator_HASHTABLE_FIND, + BuiltinOperator_HASHTABLE_IMPORT, + BuiltinOperator_HASHTABLE_SIZE, + BuiltinOperator_REDUCE_ALL, + BuiltinOperator_CONV_3D_TRANSPOSE, + BuiltinOperator_VAR_HANDLE, + BuiltinOperator_READ_VARIABLE, + BuiltinOperator_ASSIGN_VARIABLE, + BuiltinOperator_BROADCAST_ARGS, + BuiltinOperator_RANDOM_STANDARD_NORMAL}; + return values; +} + +inline const char *const *EnumNamesBuiltinOperator() +{ + static const char *const names[152] = {"BCQ_GATHER", + "BCQ_FULLY_CONNECTED", + "INSTANCE_NORM", + "", + "ADD", + "AVERAGE_POOL_2D", + "CONCATENATION", + "CONV_2D", + "DEPTHWISE_CONV_2D", + "DEPTH_TO_SPACE", + "DEQUANTIZE", + "EMBEDDING_LOOKUP", + "FLOOR", + "FULLY_CONNECTED", + "HASHTABLE_LOOKUP", + "L2_NORMALIZATION", + "L2_POOL_2D", + "LOCAL_RESPONSE_NORMALIZATION", + "LOGISTIC", + "LSH_PROJECTION", + "LSTM", + "MAX_POOL_2D", + "MUL", + "RELU", + "RELU_N1_TO_1", + "RELU6", + "RESHAPE", + "RESIZE_BILINEAR", + "RNN", + "SOFTMAX", + "SPACE_TO_DEPTH", + "SVDF", + "TANH", + "CONCAT_EMBEDDINGS", + "SKIP_GRAM", + "CALL", + "CUSTOM", + "EMBEDDING_LOOKUP_SPARSE", + "PAD", + "UNIDIRECTIONAL_SEQUENCE_RNN", + "GATHER", + "BATCH_TO_SPACE_ND", + "SPACE_TO_BATCH_ND", + "TRANSPOSE", + "MEAN", + "SUB", + "DIV", + "SQUEEZE", + "UNIDIRECTIONAL_SEQUENCE_LSTM", + "STRIDED_SLICE", + "BIDIRECTIONAL_SEQUENCE_RNN", + "EXP", + "TOPK_V2", + "SPLIT", + "LOG_SOFTMAX", + "DELEGATE", + "BIDIRECTIONAL_SEQUENCE_LSTM", + "CAST", + "PRELU", + "MAXIMUM", + "ARG_MAX", + "MINIMUM", + "LESS", + "NEG", + "PADV2", + "GREATER", + "GREATER_EQUAL", + "LESS_EQUAL", + "SELECT", + "SLICE", + "SIN", + "TRANSPOSE_CONV", + "SPARSE_TO_DENSE", + "TILE", + "EXPAND_DIMS", + "EQUAL", + "NOT_EQUAL", + "LOG", + "SUM", + "SQRT", + "RSQRT", + "SHAPE", + "POW", + "ARG_MIN", + "FAKE_QUANT", + "REDUCE_PROD", + "REDUCE_MAX", + "PACK", + "LOGICAL_OR", + "ONE_HOT", + "LOGICAL_AND", + "LOGICAL_NOT", + "UNPACK", + "REDUCE_MIN", + "FLOOR_DIV", + "REDUCE_ANY", + "SQUARE", + "ZEROS_LIKE", + "FILL", + "FLOOR_MOD", + "RANGE", + "RESIZE_NEAREST_NEIGHBOR", + "LEAKY_RELU", + "SQUARED_DIFFERENCE", + "MIRROR_PAD", + "ABS", + "SPLIT_V", + "UNIQUE", + "CEIL", + "REVERSE_V2", + "ADD_N", + "GATHER_ND", + "COS", + "WHERE", + "RANK", + "ELU", + "REVERSE_SEQUENCE", + "MATRIX_DIAG", + "QUANTIZE", + "MATRIX_SET_DIAG", + "ROUND", + "HARD_SWISH", + "IF", + "WHILE", + "NON_MAX_SUPPRESSION_V4", + "NON_MAX_SUPPRESSION_V5", + "SCATTER_ND", + "SELECT_V2", + "DENSIFY", + "SEGMENT_SUM", + "BATCH_MATMUL", + "PLACEHOLDER_FOR_GREATER_OP_CODES", + "CUMSUM", + "CALL_ONCE", + "BROADCAST_TO", + "RFFT2D", + "CONV_3D", + "IMAG", + "REAL", + "COMPLEX_ABS", + "HASHTABLE", + "HASHTABLE_FIND", + "HASHTABLE_IMPORT", + "HASHTABLE_SIZE", + "REDUCE_ALL", + "CONV_3D_TRANSPOSE", + "VAR_HANDLE", + "READ_VARIABLE", + "ASSIGN_VARIABLE", + "BROADCAST_ARGS", + "RANDOM_STANDARD_NORMAL", + nullptr}; + return names; +} + +inline const char *EnumNameBuiltinOperator(BuiltinOperator e) +{ + if (flatbuffers::IsOutRange(e, BuiltinOperator_BCQ_GATHER, + BuiltinOperator_RANDOM_STANDARD_NORMAL)) + return ""; + const size_t index = static_cast(e) - static_cast(BuiltinOperator_BCQ_GATHER); + return EnumNamesBuiltinOperator()[index]; +} + +enum BuiltinOptions : uint8_t +{ + BuiltinOptions_NONE = 0, + BuiltinOptions_Conv2DOptions = 1, + BuiltinOptions_DepthwiseConv2DOptions = 2, + BuiltinOptions_ConcatEmbeddingsOptions = 3, + BuiltinOptions_LSHProjectionOptions = 4, + BuiltinOptions_Pool2DOptions = 5, + BuiltinOptions_SVDFOptions = 6, + BuiltinOptions_RNNOptions = 7, + BuiltinOptions_FullyConnectedOptions = 8, + BuiltinOptions_SoftmaxOptions = 9, + BuiltinOptions_ConcatenationOptions = 10, + BuiltinOptions_AddOptions = 11, + BuiltinOptions_L2NormOptions = 12, + BuiltinOptions_LocalResponseNormalizationOptions = 13, + BuiltinOptions_LSTMOptions = 14, + BuiltinOptions_ResizeBilinearOptions = 15, + BuiltinOptions_CallOptions = 16, + BuiltinOptions_ReshapeOptions = 17, + BuiltinOptions_SkipGramOptions = 18, + BuiltinOptions_SpaceToDepthOptions = 19, + BuiltinOptions_EmbeddingLookupSparseOptions = 20, + BuiltinOptions_MulOptions = 21, + BuiltinOptions_PadOptions = 22, + BuiltinOptions_GatherOptions = 23, + BuiltinOptions_BatchToSpaceNDOptions = 24, + BuiltinOptions_SpaceToBatchNDOptions = 25, + BuiltinOptions_TransposeOptions = 26, + BuiltinOptions_ReducerOptions = 27, + BuiltinOptions_SubOptions = 28, + BuiltinOptions_DivOptions = 29, + BuiltinOptions_SqueezeOptions = 30, + BuiltinOptions_SequenceRNNOptions = 31, + BuiltinOptions_StridedSliceOptions = 32, + BuiltinOptions_ExpOptions = 33, + BuiltinOptions_TopKV2Options = 34, + BuiltinOptions_SplitOptions = 35, + BuiltinOptions_LogSoftmaxOptions = 36, + BuiltinOptions_CastOptions = 37, + BuiltinOptions_DequantizeOptions = 38, + BuiltinOptions_MaximumMinimumOptions = 39, + BuiltinOptions_ArgMaxOptions = 40, + BuiltinOptions_LessOptions = 41, + BuiltinOptions_NegOptions = 42, + BuiltinOptions_PadV2Options = 43, + BuiltinOptions_GreaterOptions = 44, + BuiltinOptions_GreaterEqualOptions = 45, + BuiltinOptions_LessEqualOptions = 46, + BuiltinOptions_SelectOptions = 47, + BuiltinOptions_SliceOptions = 48, + BuiltinOptions_TransposeConvOptions = 49, + BuiltinOptions_SparseToDenseOptions = 50, + BuiltinOptions_TileOptions = 51, + BuiltinOptions_ExpandDimsOptions = 52, + BuiltinOptions_EqualOptions = 53, + BuiltinOptions_NotEqualOptions = 54, + BuiltinOptions_ShapeOptions = 55, + BuiltinOptions_PowOptions = 56, + BuiltinOptions_ArgMinOptions = 57, + BuiltinOptions_FakeQuantOptions = 58, + BuiltinOptions_PackOptions = 59, + BuiltinOptions_LogicalOrOptions = 60, + BuiltinOptions_OneHotOptions = 61, + BuiltinOptions_LogicalAndOptions = 62, + BuiltinOptions_LogicalNotOptions = 63, + BuiltinOptions_UnpackOptions = 64, + BuiltinOptions_FloorDivOptions = 65, + BuiltinOptions_SquareOptions = 66, + BuiltinOptions_ZerosLikeOptions = 67, + BuiltinOptions_FillOptions = 68, + BuiltinOptions_BidirectionalSequenceLSTMOptions = 69, + BuiltinOptions_BidirectionalSequenceRNNOptions = 70, + BuiltinOptions_UnidirectionalSequenceLSTMOptions = 71, + BuiltinOptions_FloorModOptions = 72, + BuiltinOptions_RangeOptions = 73, + BuiltinOptions_ResizeNearestNeighborOptions = 74, + BuiltinOptions_LeakyReluOptions = 75, + BuiltinOptions_SquaredDifferenceOptions = 76, + BuiltinOptions_MirrorPadOptions = 77, + BuiltinOptions_AbsOptions = 78, + BuiltinOptions_SplitVOptions = 79, + BuiltinOptions_UniqueOptions = 80, + BuiltinOptions_ReverseV2Options = 81, + BuiltinOptions_AddNOptions = 82, + BuiltinOptions_GatherNdOptions = 83, + BuiltinOptions_CosOptions = 84, + BuiltinOptions_WhereOptions = 85, + BuiltinOptions_RankOptions = 86, + BuiltinOptions_ReverseSequenceOptions = 87, + BuiltinOptions_MatrixDiagOptions = 88, + BuiltinOptions_QuantizeOptions = 89, + BuiltinOptions_MatrixSetDiagOptions = 90, + BuiltinOptions_HardSwishOptions = 91, + BuiltinOptions_IfOptions = 92, + BuiltinOptions_WhileOptions = 93, + BuiltinOptions_DepthToSpaceOptions = 94, + BuiltinOptions_NonMaxSuppressionV4Options = 95, + BuiltinOptions_NonMaxSuppressionV5Options = 96, + BuiltinOptions_ScatterNdOptions = 97, + BuiltinOptions_SelectV2Options = 98, + BuiltinOptions_DensifyOptions = 99, + BuiltinOptions_SegmentSumOptions = 100, + BuiltinOptions_BatchMatMulOptions = 101, + BuiltinOptions_CumsumOptions = 102, + BuiltinOptions_CallOnceOptions = 103, + BuiltinOptions_BroadcastToOptions = 104, + BuiltinOptions_Rfft2dOptions = 105, + BuiltinOptions_Conv3DOptions = 106, + BuiltinOptions_HashtableOptions = 107, + BuiltinOptions_HashtableFindOptions = 108, + BuiltinOptions_HashtableImportOptions = 109, + BuiltinOptions_HashtableSizeOptions = 110, + BuiltinOptions_VarHandleOptions = 111, + BuiltinOptions_ReadVariableOptions = 112, + BuiltinOptions_AssignVariableOptions = 113, + BuiltinOptions_RandomOptions = 114, + BuiltinOptions_BCQGatherOptions = 252, + BuiltinOptions_BCQFullyConnectedOptions = 253, + BuiltinOptions_InstanceNormOptions = 254, + BuiltinOptions_MIN = BuiltinOptions_NONE, + BuiltinOptions_MAX = BuiltinOptions_InstanceNormOptions +}; + +inline const BuiltinOptions (&EnumValuesBuiltinOptions())[118] +{ + static const BuiltinOptions values[] = {BuiltinOptions_NONE, + BuiltinOptions_Conv2DOptions, + BuiltinOptions_DepthwiseConv2DOptions, + BuiltinOptions_ConcatEmbeddingsOptions, + BuiltinOptions_LSHProjectionOptions, + BuiltinOptions_Pool2DOptions, + BuiltinOptions_SVDFOptions, + BuiltinOptions_RNNOptions, + BuiltinOptions_FullyConnectedOptions, + BuiltinOptions_SoftmaxOptions, + BuiltinOptions_ConcatenationOptions, + BuiltinOptions_AddOptions, + BuiltinOptions_L2NormOptions, + BuiltinOptions_LocalResponseNormalizationOptions, + BuiltinOptions_LSTMOptions, + BuiltinOptions_ResizeBilinearOptions, + BuiltinOptions_CallOptions, + BuiltinOptions_ReshapeOptions, + BuiltinOptions_SkipGramOptions, + BuiltinOptions_SpaceToDepthOptions, + BuiltinOptions_EmbeddingLookupSparseOptions, + BuiltinOptions_MulOptions, + BuiltinOptions_PadOptions, + BuiltinOptions_GatherOptions, + BuiltinOptions_BatchToSpaceNDOptions, + BuiltinOptions_SpaceToBatchNDOptions, + BuiltinOptions_TransposeOptions, + BuiltinOptions_ReducerOptions, + BuiltinOptions_SubOptions, + BuiltinOptions_DivOptions, + BuiltinOptions_SqueezeOptions, + BuiltinOptions_SequenceRNNOptions, + BuiltinOptions_StridedSliceOptions, + BuiltinOptions_ExpOptions, + BuiltinOptions_TopKV2Options, + BuiltinOptions_SplitOptions, + BuiltinOptions_LogSoftmaxOptions, + BuiltinOptions_CastOptions, + BuiltinOptions_DequantizeOptions, + BuiltinOptions_MaximumMinimumOptions, + BuiltinOptions_ArgMaxOptions, + BuiltinOptions_LessOptions, + BuiltinOptions_NegOptions, + BuiltinOptions_PadV2Options, + BuiltinOptions_GreaterOptions, + BuiltinOptions_GreaterEqualOptions, + BuiltinOptions_LessEqualOptions, + BuiltinOptions_SelectOptions, + BuiltinOptions_SliceOptions, + BuiltinOptions_TransposeConvOptions, + BuiltinOptions_SparseToDenseOptions, + BuiltinOptions_TileOptions, + BuiltinOptions_ExpandDimsOptions, + BuiltinOptions_EqualOptions, + BuiltinOptions_NotEqualOptions, + BuiltinOptions_ShapeOptions, + BuiltinOptions_PowOptions, + BuiltinOptions_ArgMinOptions, + BuiltinOptions_FakeQuantOptions, + BuiltinOptions_PackOptions, + BuiltinOptions_LogicalOrOptions, + BuiltinOptions_OneHotOptions, + BuiltinOptions_LogicalAndOptions, + BuiltinOptions_LogicalNotOptions, + BuiltinOptions_UnpackOptions, + BuiltinOptions_FloorDivOptions, + BuiltinOptions_SquareOptions, + BuiltinOptions_ZerosLikeOptions, + BuiltinOptions_FillOptions, + BuiltinOptions_BidirectionalSequenceLSTMOptions, + BuiltinOptions_BidirectionalSequenceRNNOptions, + BuiltinOptions_UnidirectionalSequenceLSTMOptions, + BuiltinOptions_FloorModOptions, + BuiltinOptions_RangeOptions, + BuiltinOptions_ResizeNearestNeighborOptions, + BuiltinOptions_LeakyReluOptions, + BuiltinOptions_SquaredDifferenceOptions, + BuiltinOptions_MirrorPadOptions, + BuiltinOptions_AbsOptions, + BuiltinOptions_SplitVOptions, + BuiltinOptions_UniqueOptions, + BuiltinOptions_ReverseV2Options, + BuiltinOptions_AddNOptions, + BuiltinOptions_GatherNdOptions, + BuiltinOptions_CosOptions, + BuiltinOptions_WhereOptions, + BuiltinOptions_RankOptions, + BuiltinOptions_ReverseSequenceOptions, + BuiltinOptions_MatrixDiagOptions, + BuiltinOptions_QuantizeOptions, + BuiltinOptions_MatrixSetDiagOptions, + BuiltinOptions_HardSwishOptions, + BuiltinOptions_IfOptions, + BuiltinOptions_WhileOptions, + BuiltinOptions_DepthToSpaceOptions, + BuiltinOptions_NonMaxSuppressionV4Options, + BuiltinOptions_NonMaxSuppressionV5Options, + BuiltinOptions_ScatterNdOptions, + BuiltinOptions_SelectV2Options, + BuiltinOptions_DensifyOptions, + BuiltinOptions_SegmentSumOptions, + BuiltinOptions_BatchMatMulOptions, + BuiltinOptions_CumsumOptions, + BuiltinOptions_CallOnceOptions, + BuiltinOptions_BroadcastToOptions, + BuiltinOptions_Rfft2dOptions, + BuiltinOptions_Conv3DOptions, + BuiltinOptions_HashtableOptions, + BuiltinOptions_HashtableFindOptions, + BuiltinOptions_HashtableImportOptions, + BuiltinOptions_HashtableSizeOptions, + BuiltinOptions_VarHandleOptions, + BuiltinOptions_ReadVariableOptions, + BuiltinOptions_AssignVariableOptions, + BuiltinOptions_RandomOptions, + BuiltinOptions_BCQGatherOptions, + BuiltinOptions_BCQFullyConnectedOptions, + BuiltinOptions_InstanceNormOptions}; + return values; +} + +inline const char *const *EnumNamesBuiltinOptions() +{ + static const char *const names[256] = {"NONE", + "Conv2DOptions", + "DepthwiseConv2DOptions", + "ConcatEmbeddingsOptions", + "LSHProjectionOptions", + "Pool2DOptions", + "SVDFOptions", + "RNNOptions", + "FullyConnectedOptions", + "SoftmaxOptions", + "ConcatenationOptions", + "AddOptions", + "L2NormOptions", + "LocalResponseNormalizationOptions", + "LSTMOptions", + "ResizeBilinearOptions", + "CallOptions", + "ReshapeOptions", + "SkipGramOptions", + "SpaceToDepthOptions", + "EmbeddingLookupSparseOptions", + "MulOptions", + "PadOptions", + "GatherOptions", + "BatchToSpaceNDOptions", + "SpaceToBatchNDOptions", + "TransposeOptions", + "ReducerOptions", + "SubOptions", + "DivOptions", + "SqueezeOptions", + "SequenceRNNOptions", + "StridedSliceOptions", + "ExpOptions", + "TopKV2Options", + "SplitOptions", + "LogSoftmaxOptions", + "CastOptions", + "DequantizeOptions", + "MaximumMinimumOptions", + "ArgMaxOptions", + "LessOptions", + "NegOptions", + "PadV2Options", + "GreaterOptions", + "GreaterEqualOptions", + "LessEqualOptions", + "SelectOptions", + "SliceOptions", + "TransposeConvOptions", + "SparseToDenseOptions", + "TileOptions", + "ExpandDimsOptions", + "EqualOptions", + "NotEqualOptions", + "ShapeOptions", + "PowOptions", + "ArgMinOptions", + "FakeQuantOptions", + "PackOptions", + "LogicalOrOptions", + "OneHotOptions", + "LogicalAndOptions", + "LogicalNotOptions", + "UnpackOptions", + "FloorDivOptions", + "SquareOptions", + "ZerosLikeOptions", + "FillOptions", + "BidirectionalSequenceLSTMOptions", + "BidirectionalSequenceRNNOptions", + "UnidirectionalSequenceLSTMOptions", + "FloorModOptions", + "RangeOptions", + "ResizeNearestNeighborOptions", + "LeakyReluOptions", + "SquaredDifferenceOptions", + "MirrorPadOptions", + "AbsOptions", + "SplitVOptions", + "UniqueOptions", + "ReverseV2Options", + "AddNOptions", + "GatherNdOptions", + "CosOptions", + "WhereOptions", + "RankOptions", + "ReverseSequenceOptions", + "MatrixDiagOptions", + "QuantizeOptions", + "MatrixSetDiagOptions", + "HardSwishOptions", + "IfOptions", + "WhileOptions", + "DepthToSpaceOptions", + "NonMaxSuppressionV4Options", + "NonMaxSuppressionV5Options", + "ScatterNdOptions", + "SelectV2Options", + "DensifyOptions", + "SegmentSumOptions", + "BatchMatMulOptions", + "CumsumOptions", + "CallOnceOptions", + "BroadcastToOptions", + "Rfft2dOptions", + "Conv3DOptions", + "HashtableOptions", + "HashtableFindOptions", + "HashtableImportOptions", + "HashtableSizeOptions", + "VarHandleOptions", + "ReadVariableOptions", + "AssignVariableOptions", + "RandomOptions", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "BCQGatherOptions", + "BCQFullyConnectedOptions", + "InstanceNormOptions", + nullptr}; + return names; +} + +inline const char *EnumNameBuiltinOptions(BuiltinOptions e) +{ + if (flatbuffers::IsOutRange(e, BuiltinOptions_NONE, BuiltinOptions_InstanceNormOptions)) + return ""; + const size_t index = static_cast(e); + return EnumNamesBuiltinOptions()[index]; +} + +template struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_NONE; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_Conv2DOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_DepthwiseConv2DOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ConcatEmbeddingsOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LSHProjectionOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_Pool2DOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SVDFOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_RNNOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_FullyConnectedOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SoftmaxOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ConcatenationOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_AddOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_L2NormOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LocalResponseNormalizationOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LSTMOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ResizeBilinearOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_CallOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ReshapeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SkipGramOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SpaceToDepthOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_EmbeddingLookupSparseOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_MulOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_PadOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_GatherOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BatchToSpaceNDOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SpaceToBatchNDOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_TransposeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ReducerOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SubOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_DivOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SqueezeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SequenceRNNOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_StridedSliceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ExpOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_TopKV2Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SplitOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LogSoftmaxOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_CastOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_DequantizeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_MaximumMinimumOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ArgMaxOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LessOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_NegOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_PadV2Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_GreaterOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_GreaterEqualOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LessEqualOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SelectOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SliceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_TransposeConvOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SparseToDenseOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_TileOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ExpandDimsOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_EqualOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_NotEqualOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ShapeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_PowOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ArgMinOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_FakeQuantOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_PackOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LogicalOrOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_OneHotOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LogicalAndOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LogicalNotOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_UnpackOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_FloorDivOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SquareOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ZerosLikeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_FillOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BidirectionalSequenceLSTMOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BidirectionalSequenceRNNOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_UnidirectionalSequenceLSTMOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_FloorModOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_RangeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ResizeNearestNeighborOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_LeakyReluOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SquaredDifferenceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_MirrorPadOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_AbsOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SplitVOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_UniqueOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ReverseV2Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_AddNOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_GatherNdOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_CosOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_WhereOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_RankOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ReverseSequenceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_MatrixDiagOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_QuantizeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_MatrixSetDiagOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_HardSwishOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_IfOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_WhileOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_DepthToSpaceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_NonMaxSuppressionV4Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_NonMaxSuppressionV5Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ScatterNdOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SelectV2Options; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_DensifyOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_SegmentSumOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BatchMatMulOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_CumsumOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_CallOnceOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BroadcastToOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_Rfft2dOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_Conv3DOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_HashtableOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_HashtableFindOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_HashtableImportOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_HashtableSizeOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_VarHandleOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_ReadVariableOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_AssignVariableOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_RandomOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BCQGatherOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_BCQFullyConnectedOptions; +}; + +template <> struct BuiltinOptionsTraits +{ + static const BuiltinOptions enum_value = BuiltinOptions_InstanceNormOptions; +}; + +struct BuiltinOptionsUnion +{ + BuiltinOptions type; + void *value; + + BuiltinOptionsUnion() : type(BuiltinOptions_NONE), value(nullptr) {} + BuiltinOptionsUnion(BuiltinOptionsUnion &&u) FLATBUFFERS_NOEXCEPT : type(BuiltinOptions_NONE), + value(nullptr) + { + std::swap(type, u.type); + std::swap(value, u.value); + } + BuiltinOptionsUnion(const BuiltinOptionsUnion &); + BuiltinOptionsUnion &operator=(const BuiltinOptionsUnion &u) + { + BuiltinOptionsUnion t(u); + std::swap(type, t.type); + std::swap(value, t.value); + return *this; + } + BuiltinOptionsUnion &operator=(BuiltinOptionsUnion &&u) FLATBUFFERS_NOEXCEPT + { + std::swap(type, u.type); + std::swap(value, u.value); + return *this; + } + ~BuiltinOptionsUnion() { Reset(); } + + void Reset(); + +#ifndef FLATBUFFERS_CPP98_STL + template void Set(T &&val) + { + using RT = typename std::remove_reference::type; + Reset(); + type = BuiltinOptionsTraits::enum_value; + if (type != BuiltinOptions_NONE) + { + value = new RT(std::forward(val)); + } + } +#endif // FLATBUFFERS_CPP98_STL + + static void *UnPack(const void *obj, BuiltinOptions type, + const flatbuffers::resolver_function_t *resolver); + flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher = nullptr) const; + + circle::Conv2DOptionsT *AsConv2DOptions() + { + return type == BuiltinOptions_Conv2DOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::Conv2DOptionsT *AsConv2DOptions() const + { + return type == BuiltinOptions_Conv2DOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::DepthwiseConv2DOptionsT *AsDepthwiseConv2DOptions() + { + return type == BuiltinOptions_DepthwiseConv2DOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::DepthwiseConv2DOptionsT *AsDepthwiseConv2DOptions() const + { + return type == BuiltinOptions_DepthwiseConv2DOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ConcatEmbeddingsOptionsT *AsConcatEmbeddingsOptions() + { + return type == BuiltinOptions_ConcatEmbeddingsOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ConcatEmbeddingsOptionsT *AsConcatEmbeddingsOptions() const + { + return type == BuiltinOptions_ConcatEmbeddingsOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LSHProjectionOptionsT *AsLSHProjectionOptions() + { + return type == BuiltinOptions_LSHProjectionOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LSHProjectionOptionsT *AsLSHProjectionOptions() const + { + return type == BuiltinOptions_LSHProjectionOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::Pool2DOptionsT *AsPool2DOptions() + { + return type == BuiltinOptions_Pool2DOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::Pool2DOptionsT *AsPool2DOptions() const + { + return type == BuiltinOptions_Pool2DOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SVDFOptionsT *AsSVDFOptions() + { + return type == BuiltinOptions_SVDFOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SVDFOptionsT *AsSVDFOptions() const + { + return type == BuiltinOptions_SVDFOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::RNNOptionsT *AsRNNOptions() + { + return type == BuiltinOptions_RNNOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::RNNOptionsT *AsRNNOptions() const + { + return type == BuiltinOptions_RNNOptions ? reinterpret_cast(value) + : nullptr; + } + circle::FullyConnectedOptionsT *AsFullyConnectedOptions() + { + return type == BuiltinOptions_FullyConnectedOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::FullyConnectedOptionsT *AsFullyConnectedOptions() const + { + return type == BuiltinOptions_FullyConnectedOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SoftmaxOptionsT *AsSoftmaxOptions() + { + return type == BuiltinOptions_SoftmaxOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SoftmaxOptionsT *AsSoftmaxOptions() const + { + return type == BuiltinOptions_SoftmaxOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ConcatenationOptionsT *AsConcatenationOptions() + { + return type == BuiltinOptions_ConcatenationOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ConcatenationOptionsT *AsConcatenationOptions() const + { + return type == BuiltinOptions_ConcatenationOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::AddOptionsT *AsAddOptions() + { + return type == BuiltinOptions_AddOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::AddOptionsT *AsAddOptions() const + { + return type == BuiltinOptions_AddOptions ? reinterpret_cast(value) + : nullptr; + } + circle::L2NormOptionsT *AsL2NormOptions() + { + return type == BuiltinOptions_L2NormOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::L2NormOptionsT *AsL2NormOptions() const + { + return type == BuiltinOptions_L2NormOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LocalResponseNormalizationOptionsT *AsLocalResponseNormalizationOptions() + { + return type == BuiltinOptions_LocalResponseNormalizationOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LocalResponseNormalizationOptionsT *AsLocalResponseNormalizationOptions() const + { + return type == BuiltinOptions_LocalResponseNormalizationOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LSTMOptionsT *AsLSTMOptions() + { + return type == BuiltinOptions_LSTMOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::LSTMOptionsT *AsLSTMOptions() const + { + return type == BuiltinOptions_LSTMOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ResizeBilinearOptionsT *AsResizeBilinearOptions() + { + return type == BuiltinOptions_ResizeBilinearOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ResizeBilinearOptionsT *AsResizeBilinearOptions() const + { + return type == BuiltinOptions_ResizeBilinearOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::CallOptionsT *AsCallOptions() + { + return type == BuiltinOptions_CallOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::CallOptionsT *AsCallOptions() const + { + return type == BuiltinOptions_CallOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ReshapeOptionsT *AsReshapeOptions() + { + return type == BuiltinOptions_ReshapeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ReshapeOptionsT *AsReshapeOptions() const + { + return type == BuiltinOptions_ReshapeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SkipGramOptionsT *AsSkipGramOptions() + { + return type == BuiltinOptions_SkipGramOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SkipGramOptionsT *AsSkipGramOptions() const + { + return type == BuiltinOptions_SkipGramOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SpaceToDepthOptionsT *AsSpaceToDepthOptions() + { + return type == BuiltinOptions_SpaceToDepthOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SpaceToDepthOptionsT *AsSpaceToDepthOptions() const + { + return type == BuiltinOptions_SpaceToDepthOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::EmbeddingLookupSparseOptionsT *AsEmbeddingLookupSparseOptions() + { + return type == BuiltinOptions_EmbeddingLookupSparseOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::EmbeddingLookupSparseOptionsT *AsEmbeddingLookupSparseOptions() const + { + return type == BuiltinOptions_EmbeddingLookupSparseOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::MulOptionsT *AsMulOptions() + { + return type == BuiltinOptions_MulOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::MulOptionsT *AsMulOptions() const + { + return type == BuiltinOptions_MulOptions ? reinterpret_cast(value) + : nullptr; + } + circle::PadOptionsT *AsPadOptions() + { + return type == BuiltinOptions_PadOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::PadOptionsT *AsPadOptions() const + { + return type == BuiltinOptions_PadOptions ? reinterpret_cast(value) + : nullptr; + } + circle::GatherOptionsT *AsGatherOptions() + { + return type == BuiltinOptions_GatherOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::GatherOptionsT *AsGatherOptions() const + { + return type == BuiltinOptions_GatherOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BatchToSpaceNDOptionsT *AsBatchToSpaceNDOptions() + { + return type == BuiltinOptions_BatchToSpaceNDOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BatchToSpaceNDOptionsT *AsBatchToSpaceNDOptions() const + { + return type == BuiltinOptions_BatchToSpaceNDOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SpaceToBatchNDOptionsT *AsSpaceToBatchNDOptions() + { + return type == BuiltinOptions_SpaceToBatchNDOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SpaceToBatchNDOptionsT *AsSpaceToBatchNDOptions() const + { + return type == BuiltinOptions_SpaceToBatchNDOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::TransposeOptionsT *AsTransposeOptions() + { + return type == BuiltinOptions_TransposeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::TransposeOptionsT *AsTransposeOptions() const + { + return type == BuiltinOptions_TransposeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ReducerOptionsT *AsReducerOptions() + { + return type == BuiltinOptions_ReducerOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ReducerOptionsT *AsReducerOptions() const + { + return type == BuiltinOptions_ReducerOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SubOptionsT *AsSubOptions() + { + return type == BuiltinOptions_SubOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SubOptionsT *AsSubOptions() const + { + return type == BuiltinOptions_SubOptions ? reinterpret_cast(value) + : nullptr; + } + circle::DivOptionsT *AsDivOptions() + { + return type == BuiltinOptions_DivOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::DivOptionsT *AsDivOptions() const + { + return type == BuiltinOptions_DivOptions ? reinterpret_cast(value) + : nullptr; + } + circle::SqueezeOptionsT *AsSqueezeOptions() + { + return type == BuiltinOptions_SqueezeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SqueezeOptionsT *AsSqueezeOptions() const + { + return type == BuiltinOptions_SqueezeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SequenceRNNOptionsT *AsSequenceRNNOptions() + { + return type == BuiltinOptions_SequenceRNNOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SequenceRNNOptionsT *AsSequenceRNNOptions() const + { + return type == BuiltinOptions_SequenceRNNOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::StridedSliceOptionsT *AsStridedSliceOptions() + { + return type == BuiltinOptions_StridedSliceOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::StridedSliceOptionsT *AsStridedSliceOptions() const + { + return type == BuiltinOptions_StridedSliceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ExpOptionsT *AsExpOptions() + { + return type == BuiltinOptions_ExpOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::ExpOptionsT *AsExpOptions() const + { + return type == BuiltinOptions_ExpOptions ? reinterpret_cast(value) + : nullptr; + } + circle::TopKV2OptionsT *AsTopKV2Options() + { + return type == BuiltinOptions_TopKV2Options ? reinterpret_cast(value) + : nullptr; + } + const circle::TopKV2OptionsT *AsTopKV2Options() const + { + return type == BuiltinOptions_TopKV2Options + ? reinterpret_cast(value) + : nullptr; + } + circle::SplitOptionsT *AsSplitOptions() + { + return type == BuiltinOptions_SplitOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SplitOptionsT *AsSplitOptions() const + { + return type == BuiltinOptions_SplitOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LogSoftmaxOptionsT *AsLogSoftmaxOptions() + { + return type == BuiltinOptions_LogSoftmaxOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LogSoftmaxOptionsT *AsLogSoftmaxOptions() const + { + return type == BuiltinOptions_LogSoftmaxOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::CastOptionsT *AsCastOptions() + { + return type == BuiltinOptions_CastOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::CastOptionsT *AsCastOptions() const + { + return type == BuiltinOptions_CastOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::DequantizeOptionsT *AsDequantizeOptions() + { + return type == BuiltinOptions_DequantizeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::DequantizeOptionsT *AsDequantizeOptions() const + { + return type == BuiltinOptions_DequantizeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::MaximumMinimumOptionsT *AsMaximumMinimumOptions() + { + return type == BuiltinOptions_MaximumMinimumOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::MaximumMinimumOptionsT *AsMaximumMinimumOptions() const + { + return type == BuiltinOptions_MaximumMinimumOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ArgMaxOptionsT *AsArgMaxOptions() + { + return type == BuiltinOptions_ArgMaxOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::ArgMaxOptionsT *AsArgMaxOptions() const + { + return type == BuiltinOptions_ArgMaxOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LessOptionsT *AsLessOptions() + { + return type == BuiltinOptions_LessOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::LessOptionsT *AsLessOptions() const + { + return type == BuiltinOptions_LessOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::NegOptionsT *AsNegOptions() + { + return type == BuiltinOptions_NegOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::NegOptionsT *AsNegOptions() const + { + return type == BuiltinOptions_NegOptions ? reinterpret_cast(value) + : nullptr; + } + circle::PadV2OptionsT *AsPadV2Options() + { + return type == BuiltinOptions_PadV2Options ? reinterpret_cast(value) + : nullptr; + } + const circle::PadV2OptionsT *AsPadV2Options() const + { + return type == BuiltinOptions_PadV2Options + ? reinterpret_cast(value) + : nullptr; + } + circle::GreaterOptionsT *AsGreaterOptions() + { + return type == BuiltinOptions_GreaterOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::GreaterOptionsT *AsGreaterOptions() const + { + return type == BuiltinOptions_GreaterOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::GreaterEqualOptionsT *AsGreaterEqualOptions() + { + return type == BuiltinOptions_GreaterEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::GreaterEqualOptionsT *AsGreaterEqualOptions() const + { + return type == BuiltinOptions_GreaterEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LessEqualOptionsT *AsLessEqualOptions() + { + return type == BuiltinOptions_LessEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LessEqualOptionsT *AsLessEqualOptions() const + { + return type == BuiltinOptions_LessEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SelectOptionsT *AsSelectOptions() + { + return type == BuiltinOptions_SelectOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SelectOptionsT *AsSelectOptions() const + { + return type == BuiltinOptions_SelectOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SliceOptionsT *AsSliceOptions() + { + return type == BuiltinOptions_SliceOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SliceOptionsT *AsSliceOptions() const + { + return type == BuiltinOptions_SliceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::TransposeConvOptionsT *AsTransposeConvOptions() + { + return type == BuiltinOptions_TransposeConvOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::TransposeConvOptionsT *AsTransposeConvOptions() const + { + return type == BuiltinOptions_TransposeConvOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SparseToDenseOptionsT *AsSparseToDenseOptions() + { + return type == BuiltinOptions_SparseToDenseOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SparseToDenseOptionsT *AsSparseToDenseOptions() const + { + return type == BuiltinOptions_SparseToDenseOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::TileOptionsT *AsTileOptions() + { + return type == BuiltinOptions_TileOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::TileOptionsT *AsTileOptions() const + { + return type == BuiltinOptions_TileOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ExpandDimsOptionsT *AsExpandDimsOptions() + { + return type == BuiltinOptions_ExpandDimsOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ExpandDimsOptionsT *AsExpandDimsOptions() const + { + return type == BuiltinOptions_ExpandDimsOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::EqualOptionsT *AsEqualOptions() + { + return type == BuiltinOptions_EqualOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::EqualOptionsT *AsEqualOptions() const + { + return type == BuiltinOptions_EqualOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::NotEqualOptionsT *AsNotEqualOptions() + { + return type == BuiltinOptions_NotEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::NotEqualOptionsT *AsNotEqualOptions() const + { + return type == BuiltinOptions_NotEqualOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ShapeOptionsT *AsShapeOptions() + { + return type == BuiltinOptions_ShapeOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::ShapeOptionsT *AsShapeOptions() const + { + return type == BuiltinOptions_ShapeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::PowOptionsT *AsPowOptions() + { + return type == BuiltinOptions_PowOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::PowOptionsT *AsPowOptions() const + { + return type == BuiltinOptions_PowOptions ? reinterpret_cast(value) + : nullptr; + } + circle::ArgMinOptionsT *AsArgMinOptions() + { + return type == BuiltinOptions_ArgMinOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::ArgMinOptionsT *AsArgMinOptions() const + { + return type == BuiltinOptions_ArgMinOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::FakeQuantOptionsT *AsFakeQuantOptions() + { + return type == BuiltinOptions_FakeQuantOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::FakeQuantOptionsT *AsFakeQuantOptions() const + { + return type == BuiltinOptions_FakeQuantOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::PackOptionsT *AsPackOptions() + { + return type == BuiltinOptions_PackOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::PackOptionsT *AsPackOptions() const + { + return type == BuiltinOptions_PackOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LogicalOrOptionsT *AsLogicalOrOptions() + { + return type == BuiltinOptions_LogicalOrOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LogicalOrOptionsT *AsLogicalOrOptions() const + { + return type == BuiltinOptions_LogicalOrOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::OneHotOptionsT *AsOneHotOptions() + { + return type == BuiltinOptions_OneHotOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::OneHotOptionsT *AsOneHotOptions() const + { + return type == BuiltinOptions_OneHotOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LogicalAndOptionsT *AsLogicalAndOptions() + { + return type == BuiltinOptions_LogicalAndOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LogicalAndOptionsT *AsLogicalAndOptions() const + { + return type == BuiltinOptions_LogicalAndOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LogicalNotOptionsT *AsLogicalNotOptions() + { + return type == BuiltinOptions_LogicalNotOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LogicalNotOptionsT *AsLogicalNotOptions() const + { + return type == BuiltinOptions_LogicalNotOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::UnpackOptionsT *AsUnpackOptions() + { + return type == BuiltinOptions_UnpackOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::UnpackOptionsT *AsUnpackOptions() const + { + return type == BuiltinOptions_UnpackOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::FloorDivOptionsT *AsFloorDivOptions() + { + return type == BuiltinOptions_FloorDivOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::FloorDivOptionsT *AsFloorDivOptions() const + { + return type == BuiltinOptions_FloorDivOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SquareOptionsT *AsSquareOptions() + { + return type == BuiltinOptions_SquareOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SquareOptionsT *AsSquareOptions() const + { + return type == BuiltinOptions_SquareOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ZerosLikeOptionsT *AsZerosLikeOptions() + { + return type == BuiltinOptions_ZerosLikeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ZerosLikeOptionsT *AsZerosLikeOptions() const + { + return type == BuiltinOptions_ZerosLikeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::FillOptionsT *AsFillOptions() + { + return type == BuiltinOptions_FillOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::FillOptionsT *AsFillOptions() const + { + return type == BuiltinOptions_FillOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BidirectionalSequenceLSTMOptionsT *AsBidirectionalSequenceLSTMOptions() + { + return type == BuiltinOptions_BidirectionalSequenceLSTMOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BidirectionalSequenceLSTMOptionsT *AsBidirectionalSequenceLSTMOptions() const + { + return type == BuiltinOptions_BidirectionalSequenceLSTMOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BidirectionalSequenceRNNOptionsT *AsBidirectionalSequenceRNNOptions() + { + return type == BuiltinOptions_BidirectionalSequenceRNNOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BidirectionalSequenceRNNOptionsT *AsBidirectionalSequenceRNNOptions() const + { + return type == BuiltinOptions_BidirectionalSequenceRNNOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::UnidirectionalSequenceLSTMOptionsT *AsUnidirectionalSequenceLSTMOptions() + { + return type == BuiltinOptions_UnidirectionalSequenceLSTMOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::UnidirectionalSequenceLSTMOptionsT *AsUnidirectionalSequenceLSTMOptions() const + { + return type == BuiltinOptions_UnidirectionalSequenceLSTMOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::FloorModOptionsT *AsFloorModOptions() + { + return type == BuiltinOptions_FloorModOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::FloorModOptionsT *AsFloorModOptions() const + { + return type == BuiltinOptions_FloorModOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::RangeOptionsT *AsRangeOptions() + { + return type == BuiltinOptions_RangeOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::RangeOptionsT *AsRangeOptions() const + { + return type == BuiltinOptions_RangeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ResizeNearestNeighborOptionsT *AsResizeNearestNeighborOptions() + { + return type == BuiltinOptions_ResizeNearestNeighborOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ResizeNearestNeighborOptionsT *AsResizeNearestNeighborOptions() const + { + return type == BuiltinOptions_ResizeNearestNeighborOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::LeakyReluOptionsT *AsLeakyReluOptions() + { + return type == BuiltinOptions_LeakyReluOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::LeakyReluOptionsT *AsLeakyReluOptions() const + { + return type == BuiltinOptions_LeakyReluOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SquaredDifferenceOptionsT *AsSquaredDifferenceOptions() + { + return type == BuiltinOptions_SquaredDifferenceOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SquaredDifferenceOptionsT *AsSquaredDifferenceOptions() const + { + return type == BuiltinOptions_SquaredDifferenceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::MirrorPadOptionsT *AsMirrorPadOptions() + { + return type == BuiltinOptions_MirrorPadOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::MirrorPadOptionsT *AsMirrorPadOptions() const + { + return type == BuiltinOptions_MirrorPadOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::AbsOptionsT *AsAbsOptions() + { + return type == BuiltinOptions_AbsOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::AbsOptionsT *AsAbsOptions() const + { + return type == BuiltinOptions_AbsOptions ? reinterpret_cast(value) + : nullptr; + } + circle::SplitVOptionsT *AsSplitVOptions() + { + return type == BuiltinOptions_SplitVOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::SplitVOptionsT *AsSplitVOptions() const + { + return type == BuiltinOptions_SplitVOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::UniqueOptionsT *AsUniqueOptions() + { + return type == BuiltinOptions_UniqueOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::UniqueOptionsT *AsUniqueOptions() const + { + return type == BuiltinOptions_UniqueOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ReverseV2OptionsT *AsReverseV2Options() + { + return type == BuiltinOptions_ReverseV2Options + ? reinterpret_cast(value) + : nullptr; + } + const circle::ReverseV2OptionsT *AsReverseV2Options() const + { + return type == BuiltinOptions_ReverseV2Options + ? reinterpret_cast(value) + : nullptr; + } + circle::AddNOptionsT *AsAddNOptions() + { + return type == BuiltinOptions_AddNOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::AddNOptionsT *AsAddNOptions() const + { + return type == BuiltinOptions_AddNOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::GatherNdOptionsT *AsGatherNdOptions() + { + return type == BuiltinOptions_GatherNdOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::GatherNdOptionsT *AsGatherNdOptions() const + { + return type == BuiltinOptions_GatherNdOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::CosOptionsT *AsCosOptions() + { + return type == BuiltinOptions_CosOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::CosOptionsT *AsCosOptions() const + { + return type == BuiltinOptions_CosOptions ? reinterpret_cast(value) + : nullptr; + } + circle::WhereOptionsT *AsWhereOptions() + { + return type == BuiltinOptions_WhereOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::WhereOptionsT *AsWhereOptions() const + { + return type == BuiltinOptions_WhereOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::RankOptionsT *AsRankOptions() + { + return type == BuiltinOptions_RankOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::RankOptionsT *AsRankOptions() const + { + return type == BuiltinOptions_RankOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ReverseSequenceOptionsT *AsReverseSequenceOptions() + { + return type == BuiltinOptions_ReverseSequenceOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ReverseSequenceOptionsT *AsReverseSequenceOptions() const + { + return type == BuiltinOptions_ReverseSequenceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::MatrixDiagOptionsT *AsMatrixDiagOptions() + { + return type == BuiltinOptions_MatrixDiagOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::MatrixDiagOptionsT *AsMatrixDiagOptions() const + { + return type == BuiltinOptions_MatrixDiagOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::QuantizeOptionsT *AsQuantizeOptions() + { + return type == BuiltinOptions_QuantizeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::QuantizeOptionsT *AsQuantizeOptions() const + { + return type == BuiltinOptions_QuantizeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::MatrixSetDiagOptionsT *AsMatrixSetDiagOptions() + { + return type == BuiltinOptions_MatrixSetDiagOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::MatrixSetDiagOptionsT *AsMatrixSetDiagOptions() const + { + return type == BuiltinOptions_MatrixSetDiagOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::HardSwishOptionsT *AsHardSwishOptions() + { + return type == BuiltinOptions_HardSwishOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::HardSwishOptionsT *AsHardSwishOptions() const + { + return type == BuiltinOptions_HardSwishOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::IfOptionsT *AsIfOptions() + { + return type == BuiltinOptions_IfOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::IfOptionsT *AsIfOptions() const + { + return type == BuiltinOptions_IfOptions ? reinterpret_cast(value) + : nullptr; + } + circle::WhileOptionsT *AsWhileOptions() + { + return type == BuiltinOptions_WhileOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::WhileOptionsT *AsWhileOptions() const + { + return type == BuiltinOptions_WhileOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::DepthToSpaceOptionsT *AsDepthToSpaceOptions() + { + return type == BuiltinOptions_DepthToSpaceOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::DepthToSpaceOptionsT *AsDepthToSpaceOptions() const + { + return type == BuiltinOptions_DepthToSpaceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::NonMaxSuppressionV4OptionsT *AsNonMaxSuppressionV4Options() + { + return type == BuiltinOptions_NonMaxSuppressionV4Options + ? reinterpret_cast(value) + : nullptr; + } + const circle::NonMaxSuppressionV4OptionsT *AsNonMaxSuppressionV4Options() const + { + return type == BuiltinOptions_NonMaxSuppressionV4Options + ? reinterpret_cast(value) + : nullptr; + } + circle::NonMaxSuppressionV5OptionsT *AsNonMaxSuppressionV5Options() + { + return type == BuiltinOptions_NonMaxSuppressionV5Options + ? reinterpret_cast(value) + : nullptr; + } + const circle::NonMaxSuppressionV5OptionsT *AsNonMaxSuppressionV5Options() const + { + return type == BuiltinOptions_NonMaxSuppressionV5Options + ? reinterpret_cast(value) + : nullptr; + } + circle::ScatterNdOptionsT *AsScatterNdOptions() + { + return type == BuiltinOptions_ScatterNdOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ScatterNdOptionsT *AsScatterNdOptions() const + { + return type == BuiltinOptions_ScatterNdOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SelectV2OptionsT *AsSelectV2Options() + { + return type == BuiltinOptions_SelectV2Options + ? reinterpret_cast(value) + : nullptr; + } + const circle::SelectV2OptionsT *AsSelectV2Options() const + { + return type == BuiltinOptions_SelectV2Options + ? reinterpret_cast(value) + : nullptr; + } + circle::DensifyOptionsT *AsDensifyOptions() + { + return type == BuiltinOptions_DensifyOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::DensifyOptionsT *AsDensifyOptions() const + { + return type == BuiltinOptions_DensifyOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::SegmentSumOptionsT *AsSegmentSumOptions() + { + return type == BuiltinOptions_SegmentSumOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::SegmentSumOptionsT *AsSegmentSumOptions() const + { + return type == BuiltinOptions_SegmentSumOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BatchMatMulOptionsT *AsBatchMatMulOptions() + { + return type == BuiltinOptions_BatchMatMulOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BatchMatMulOptionsT *AsBatchMatMulOptions() const + { + return type == BuiltinOptions_BatchMatMulOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::CumsumOptionsT *AsCumsumOptions() + { + return type == BuiltinOptions_CumsumOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::CumsumOptionsT *AsCumsumOptions() const + { + return type == BuiltinOptions_CumsumOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::CallOnceOptionsT *AsCallOnceOptions() + { + return type == BuiltinOptions_CallOnceOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::CallOnceOptionsT *AsCallOnceOptions() const + { + return type == BuiltinOptions_CallOnceOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BroadcastToOptionsT *AsBroadcastToOptions() + { + return type == BuiltinOptions_BroadcastToOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BroadcastToOptionsT *AsBroadcastToOptions() const + { + return type == BuiltinOptions_BroadcastToOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::Rfft2dOptionsT *AsRfft2dOptions() + { + return type == BuiltinOptions_Rfft2dOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::Rfft2dOptionsT *AsRfft2dOptions() const + { + return type == BuiltinOptions_Rfft2dOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::Conv3DOptionsT *AsConv3DOptions() + { + return type == BuiltinOptions_Conv3DOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::Conv3DOptionsT *AsConv3DOptions() const + { + return type == BuiltinOptions_Conv3DOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::HashtableOptionsT *AsHashtableOptions() + { + return type == BuiltinOptions_HashtableOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::HashtableOptionsT *AsHashtableOptions() const + { + return type == BuiltinOptions_HashtableOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::HashtableFindOptionsT *AsHashtableFindOptions() + { + return type == BuiltinOptions_HashtableFindOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::HashtableFindOptionsT *AsHashtableFindOptions() const + { + return type == BuiltinOptions_HashtableFindOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::HashtableImportOptionsT *AsHashtableImportOptions() + { + return type == BuiltinOptions_HashtableImportOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::HashtableImportOptionsT *AsHashtableImportOptions() const + { + return type == BuiltinOptions_HashtableImportOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::HashtableSizeOptionsT *AsHashtableSizeOptions() + { + return type == BuiltinOptions_HashtableSizeOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::HashtableSizeOptionsT *AsHashtableSizeOptions() const + { + return type == BuiltinOptions_HashtableSizeOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::VarHandleOptionsT *AsVarHandleOptions() + { + return type == BuiltinOptions_VarHandleOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::VarHandleOptionsT *AsVarHandleOptions() const + { + return type == BuiltinOptions_VarHandleOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::ReadVariableOptionsT *AsReadVariableOptions() + { + return type == BuiltinOptions_ReadVariableOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::ReadVariableOptionsT *AsReadVariableOptions() const + { + return type == BuiltinOptions_ReadVariableOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::AssignVariableOptionsT *AsAssignVariableOptions() + { + return type == BuiltinOptions_AssignVariableOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::AssignVariableOptionsT *AsAssignVariableOptions() const + { + return type == BuiltinOptions_AssignVariableOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::RandomOptionsT *AsRandomOptions() + { + return type == BuiltinOptions_RandomOptions ? reinterpret_cast(value) + : nullptr; + } + const circle::RandomOptionsT *AsRandomOptions() const + { + return type == BuiltinOptions_RandomOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BCQGatherOptionsT *AsBCQGatherOptions() + { + return type == BuiltinOptions_BCQGatherOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BCQGatherOptionsT *AsBCQGatherOptions() const + { + return type == BuiltinOptions_BCQGatherOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::BCQFullyConnectedOptionsT *AsBCQFullyConnectedOptions() + { + return type == BuiltinOptions_BCQFullyConnectedOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::BCQFullyConnectedOptionsT *AsBCQFullyConnectedOptions() const + { + return type == BuiltinOptions_BCQFullyConnectedOptions + ? reinterpret_cast(value) + : nullptr; + } + circle::InstanceNormOptionsT *AsInstanceNormOptions() + { + return type == BuiltinOptions_InstanceNormOptions + ? reinterpret_cast(value) + : nullptr; + } + const circle::InstanceNormOptionsT *AsInstanceNormOptions() const + { + return type == BuiltinOptions_InstanceNormOptions + ? reinterpret_cast(value) + : nullptr; + } +}; + +bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *obj, BuiltinOptions type); +bool VerifyBuiltinOptionsVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types); + +enum Padding : int8_t +{ + Padding_SAME = 0, + Padding_VALID = 1, + Padding_MIN = Padding_SAME, + Padding_MAX = Padding_VALID +}; + +inline const Padding (&EnumValuesPadding())[2] +{ + static const Padding values[] = {Padding_SAME, Padding_VALID}; + return values; +} + +inline const char *const *EnumNamesPadding() +{ + static const char *const names[3] = {"SAME", "VALID", nullptr}; + return names; +} + +inline const char *EnumNamePadding(Padding e) +{ + if (flatbuffers::IsOutRange(e, Padding_SAME, Padding_VALID)) + return ""; + const size_t index = static_cast(e); + return EnumNamesPadding()[index]; +} + +enum ActivationFunctionType : int8_t +{ + ActivationFunctionType_NONE = 0, + ActivationFunctionType_RELU = 1, + ActivationFunctionType_RELU_N1_TO_1 = 2, + ActivationFunctionType_RELU6 = 3, + ActivationFunctionType_TANH = 4, + ActivationFunctionType_SIGN_BIT = 5, + ActivationFunctionType_MIN = ActivationFunctionType_NONE, + ActivationFunctionType_MAX = ActivationFunctionType_SIGN_BIT +}; + +inline const ActivationFunctionType (&EnumValuesActivationFunctionType())[6] +{ + static const ActivationFunctionType values[] = { + ActivationFunctionType_NONE, ActivationFunctionType_RELU, ActivationFunctionType_RELU_N1_TO_1, + ActivationFunctionType_RELU6, ActivationFunctionType_TANH, ActivationFunctionType_SIGN_BIT}; + return values; +} + +inline const char *const *EnumNamesActivationFunctionType() +{ + static const char *const names[7] = {"NONE", "RELU", "RELU_N1_TO_1", "RELU6", + "TANH", "SIGN_BIT", nullptr}; + return names; +} + +inline const char *EnumNameActivationFunctionType(ActivationFunctionType e) +{ + if (flatbuffers::IsOutRange(e, ActivationFunctionType_NONE, ActivationFunctionType_SIGN_BIT)) + return ""; + const size_t index = static_cast(e); + return EnumNamesActivationFunctionType()[index]; +} + +enum LSHProjectionType : int8_t +{ + LSHProjectionType_UNKNOWN = 0, + LSHProjectionType_SPARSE = 1, + LSHProjectionType_DENSE = 2, + LSHProjectionType_MIN = LSHProjectionType_UNKNOWN, + LSHProjectionType_MAX = LSHProjectionType_DENSE +}; + +inline const LSHProjectionType (&EnumValuesLSHProjectionType())[3] +{ + static const LSHProjectionType values[] = {LSHProjectionType_UNKNOWN, LSHProjectionType_SPARSE, + LSHProjectionType_DENSE}; + return values; +} + +inline const char *const *EnumNamesLSHProjectionType() +{ + static const char *const names[4] = {"UNKNOWN", "SPARSE", "DENSE", nullptr}; + return names; +} + +inline const char *EnumNameLSHProjectionType(LSHProjectionType e) +{ + if (flatbuffers::IsOutRange(e, LSHProjectionType_UNKNOWN, LSHProjectionType_DENSE)) + return ""; + const size_t index = static_cast(e); + return EnumNamesLSHProjectionType()[index]; +} + +enum FullyConnectedOptionsWeightsFormat : int8_t +{ + FullyConnectedOptionsWeightsFormat_DEFAULT = 0, + FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8 = 1, + FullyConnectedOptionsWeightsFormat_SHUFFLED16x1FLOAT32 = 127, + FullyConnectedOptionsWeightsFormat_MIN = FullyConnectedOptionsWeightsFormat_DEFAULT, + FullyConnectedOptionsWeightsFormat_MAX = FullyConnectedOptionsWeightsFormat_SHUFFLED16x1FLOAT32 +}; + +inline const FullyConnectedOptionsWeightsFormat (&EnumValuesFullyConnectedOptionsWeightsFormat())[3] +{ + static const FullyConnectedOptionsWeightsFormat values[] = { + FullyConnectedOptionsWeightsFormat_DEFAULT, FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8, + FullyConnectedOptionsWeightsFormat_SHUFFLED16x1FLOAT32}; + return values; +} + +inline const char *EnumNameFullyConnectedOptionsWeightsFormat(FullyConnectedOptionsWeightsFormat e) +{ + switch (e) + { + case FullyConnectedOptionsWeightsFormat_DEFAULT: + return "DEFAULT"; + case FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8: + return "SHUFFLED4x16INT8"; + case FullyConnectedOptionsWeightsFormat_SHUFFLED16x1FLOAT32: + return "SHUFFLED16x1FLOAT32"; + default: + return ""; + } +} + +enum LSTMKernelType : int8_t +{ + LSTMKernelType_FULL = 0, + LSTMKernelType_BASIC = 1, + LSTMKernelType_MIN = LSTMKernelType_FULL, + LSTMKernelType_MAX = LSTMKernelType_BASIC +}; + +inline const LSTMKernelType (&EnumValuesLSTMKernelType())[2] +{ + static const LSTMKernelType values[] = {LSTMKernelType_FULL, LSTMKernelType_BASIC}; + return values; +} + +inline const char *const *EnumNamesLSTMKernelType() +{ + static const char *const names[3] = {"FULL", "BASIC", nullptr}; + return names; +} + +inline const char *EnumNameLSTMKernelType(LSTMKernelType e) +{ + if (flatbuffers::IsOutRange(e, LSTMKernelType_FULL, LSTMKernelType_BASIC)) + return ""; + const size_t index = static_cast(e); + return EnumNamesLSTMKernelType()[index]; +} + +enum CombinerType : int8_t +{ + CombinerType_SUM = 0, + CombinerType_MEAN = 1, + CombinerType_SQRTN = 2, + CombinerType_MIN = CombinerType_SUM, + CombinerType_MAX = CombinerType_SQRTN +}; + +inline const CombinerType (&EnumValuesCombinerType())[3] +{ + static const CombinerType values[] = {CombinerType_SUM, CombinerType_MEAN, CombinerType_SQRTN}; + return values; +} + +inline const char *const *EnumNamesCombinerType() +{ + static const char *const names[4] = {"SUM", "MEAN", "SQRTN", nullptr}; + return names; +} + +inline const char *EnumNameCombinerType(CombinerType e) +{ + if (flatbuffers::IsOutRange(e, CombinerType_SUM, CombinerType_SQRTN)) + return ""; + const size_t index = static_cast(e); + return EnumNamesCombinerType()[index]; +} + +enum MirrorPadMode : int8_t +{ + MirrorPadMode_REFLECT = 0, + MirrorPadMode_SYMMETRIC = 1, + MirrorPadMode_MIN = MirrorPadMode_REFLECT, + MirrorPadMode_MAX = MirrorPadMode_SYMMETRIC +}; + +inline const MirrorPadMode (&EnumValuesMirrorPadMode())[2] +{ + static const MirrorPadMode values[] = {MirrorPadMode_REFLECT, MirrorPadMode_SYMMETRIC}; + return values; +} + +inline const char *const *EnumNamesMirrorPadMode() +{ + static const char *const names[3] = {"REFLECT", "SYMMETRIC", nullptr}; + return names; +} + +inline const char *EnumNameMirrorPadMode(MirrorPadMode e) +{ + if (flatbuffers::IsOutRange(e, MirrorPadMode_REFLECT, MirrorPadMode_SYMMETRIC)) + return ""; + const size_t index = static_cast(e); + return EnumNamesMirrorPadMode()[index]; +} + +enum CustomOptionsFormat : int8_t +{ + CustomOptionsFormat_FLEXBUFFERS = 0, + CustomOptionsFormat_MIN = CustomOptionsFormat_FLEXBUFFERS, + CustomOptionsFormat_MAX = CustomOptionsFormat_FLEXBUFFERS +}; + +inline const CustomOptionsFormat (&EnumValuesCustomOptionsFormat())[1] +{ + static const CustomOptionsFormat values[] = {CustomOptionsFormat_FLEXBUFFERS}; + return values; +} + +inline const char *const *EnumNamesCustomOptionsFormat() +{ + static const char *const names[2] = {"FLEXBUFFERS", nullptr}; + return names; +} + +inline const char *EnumNameCustomOptionsFormat(CustomOptionsFormat e) +{ + if (flatbuffers::IsOutRange(e, CustomOptionsFormat_FLEXBUFFERS, CustomOptionsFormat_FLEXBUFFERS)) + return ""; + const size_t index = static_cast(e); + return EnumNamesCustomOptionsFormat()[index]; +} + +enum DataFormat : int8_t +{ + DataFormat_CHANNELS_LAST = 0, + DataFormat_CHANNELS_FIRST = 1, + DataFormat_MIN = DataFormat_CHANNELS_LAST, + DataFormat_MAX = DataFormat_CHANNELS_FIRST +}; + +inline const DataFormat (&EnumValuesDataFormat())[2] +{ + static const DataFormat values[] = {DataFormat_CHANNELS_LAST, DataFormat_CHANNELS_FIRST}; + return values; +} + +inline const char *const *EnumNamesDataFormat() +{ + static const char *const names[3] = {"CHANNELS_LAST", "CHANNELS_FIRST", nullptr}; + return names; +} + +inline const char *EnumNameDataFormat(DataFormat e) +{ + if (flatbuffers::IsOutRange(e, DataFormat_CHANNELS_LAST, DataFormat_CHANNELS_FIRST)) + return ""; + const size_t index = static_cast(e); + return EnumNamesDataFormat()[index]; +} + +struct CustomQuantizationT : public flatbuffers::NativeTable +{ + typedef CustomQuantization TableType; + std::vector custom{}; +}; + +struct CustomQuantization FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CustomQuantizationT NativeTableType; + typedef CustomQuantizationBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_CUSTOM = 4 + }; + const flatbuffers::Vector *custom() const + { + return GetPointer *>(VT_CUSTOM); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_CUSTOM) && + verifier.VerifyVector(custom()) && verifier.EndTable(); + } + CustomQuantizationT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CustomQuantizationT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CustomQuantizationT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CustomQuantizationBuilder +{ + typedef CustomQuantization Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_custom(flatbuffers::Offset> custom) + { + fbb_.AddOffset(CustomQuantization::VT_CUSTOM, custom); + } + explicit CustomQuantizationBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateCustomQuantization(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> custom = 0) +{ + CustomQuantizationBuilder builder_(_fbb); + builder_.add_custom(custom); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateCustomQuantizationDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *custom = nullptr) +{ + if (custom) + { + _fbb.ForceVectorAlignment(custom->size(), sizeof(uint8_t), 16); + } + auto custom__ = custom ? _fbb.CreateVector(*custom) : 0; + return circle::CreateCustomQuantization(_fbb, custom__); +} + +flatbuffers::Offset +CreateCustomQuantization(flatbuffers::FlatBufferBuilder &_fbb, const CustomQuantizationT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct QuantizationParametersT : public flatbuffers::NativeTable +{ + typedef QuantizationParameters TableType; + std::vector min{}; + std::vector max{}; + std::vector scale{}; + std::vector zero_point{}; + circle::QuantizationDetailsUnion details{}; + int32_t quantized_dimension = 0; +}; + +struct QuantizationParameters FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef QuantizationParametersT NativeTableType; + typedef QuantizationParametersBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_MIN = 4, + VT_MAX = 6, + VT_SCALE = 8, + VT_ZERO_POINT = 10, + VT_DETAILS_TYPE = 12, + VT_DETAILS = 14, + VT_QUANTIZED_DIMENSION = 16 + }; + const flatbuffers::Vector *min() const + { + return GetPointer *>(VT_MIN); + } + const flatbuffers::Vector *max() const + { + return GetPointer *>(VT_MAX); + } + const flatbuffers::Vector *scale() const + { + return GetPointer *>(VT_SCALE); + } + const flatbuffers::Vector *zero_point() const + { + return GetPointer *>(VT_ZERO_POINT); + } + circle::QuantizationDetails details_type() const + { + return static_cast(GetField(VT_DETAILS_TYPE, 0)); + } + const void *details() const { return GetPointer(VT_DETAILS); } + template const T *details_as() const; + const circle::CustomQuantization *details_as_CustomQuantization() const + { + return details_type() == circle::QuantizationDetails_CustomQuantization + ? static_cast(details()) + : nullptr; + } + int32_t quantized_dimension() const { return GetField(VT_QUANTIZED_DIMENSION, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_MIN) && + verifier.VerifyVector(min()) && VerifyOffset(verifier, VT_MAX) && + verifier.VerifyVector(max()) && VerifyOffset(verifier, VT_SCALE) && + verifier.VerifyVector(scale()) && VerifyOffset(verifier, VT_ZERO_POINT) && + verifier.VerifyVector(zero_point()) && VerifyField(verifier, VT_DETAILS_TYPE) && + VerifyOffset(verifier, VT_DETAILS) && + VerifyQuantizationDetails(verifier, details(), details_type()) && + VerifyField(verifier, VT_QUANTIZED_DIMENSION) && verifier.EndTable(); + } + QuantizationParametersT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(QuantizationParametersT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const QuantizationParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +template <> +inline const circle::CustomQuantization * +QuantizationParameters::details_as() const +{ + return details_as_CustomQuantization(); +} + +struct QuantizationParametersBuilder +{ + typedef QuantizationParameters Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_min(flatbuffers::Offset> min) + { + fbb_.AddOffset(QuantizationParameters::VT_MIN, min); + } + void add_max(flatbuffers::Offset> max) + { + fbb_.AddOffset(QuantizationParameters::VT_MAX, max); + } + void add_scale(flatbuffers::Offset> scale) + { + fbb_.AddOffset(QuantizationParameters::VT_SCALE, scale); + } + void add_zero_point(flatbuffers::Offset> zero_point) + { + fbb_.AddOffset(QuantizationParameters::VT_ZERO_POINT, zero_point); + } + void add_details_type(circle::QuantizationDetails details_type) + { + fbb_.AddElement(QuantizationParameters::VT_DETAILS_TYPE, + static_cast(details_type), 0); + } + void add_details(flatbuffers::Offset details) + { + fbb_.AddOffset(QuantizationParameters::VT_DETAILS, details); + } + void add_quantized_dimension(int32_t quantized_dimension) + { + fbb_.AddElement(QuantizationParameters::VT_QUANTIZED_DIMENSION, quantized_dimension, + 0); + } + explicit QuantizationParametersBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateQuantizationParameters( + flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset> min = 0, + flatbuffers::Offset> max = 0, + flatbuffers::Offset> scale = 0, + flatbuffers::Offset> zero_point = 0, + circle::QuantizationDetails details_type = circle::QuantizationDetails_NONE, + flatbuffers::Offset details = 0, int32_t quantized_dimension = 0) +{ + QuantizationParametersBuilder builder_(_fbb); + builder_.add_quantized_dimension(quantized_dimension); + builder_.add_details(details); + builder_.add_zero_point(zero_point); + builder_.add_scale(scale); + builder_.add_max(max); + builder_.add_min(min); + builder_.add_details_type(details_type); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateQuantizationParametersDirect( + flatbuffers::FlatBufferBuilder &_fbb, const std::vector *min = nullptr, + const std::vector *max = nullptr, const std::vector *scale = nullptr, + const std::vector *zero_point = nullptr, + circle::QuantizationDetails details_type = circle::QuantizationDetails_NONE, + flatbuffers::Offset details = 0, int32_t quantized_dimension = 0) +{ + auto min__ = min ? _fbb.CreateVector(*min) : 0; + auto max__ = max ? _fbb.CreateVector(*max) : 0; + auto scale__ = scale ? _fbb.CreateVector(*scale) : 0; + auto zero_point__ = zero_point ? _fbb.CreateVector(*zero_point) : 0; + return circle::CreateQuantizationParameters(_fbb, min__, max__, scale__, zero_point__, + details_type, details, quantized_dimension); +} + +flatbuffers::Offset +CreateQuantizationParameters(flatbuffers::FlatBufferBuilder &_fbb, + const QuantizationParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Int32VectorT : public flatbuffers::NativeTable +{ + typedef Int32Vector TableType; + std::vector values{}; +}; + +struct Int32Vector FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Int32VectorT NativeTableType; + typedef Int32VectorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VALUES = 4 + }; + const flatbuffers::Vector *values() const + { + return GetPointer *>(VT_VALUES); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && verifier.EndTable(); + } + Int32VectorT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Int32VectorT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Int32VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Int32VectorBuilder +{ + typedef Int32Vector Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_values(flatbuffers::Offset> values) + { + fbb_.AddOffset(Int32Vector::VT_VALUES, values); + } + explicit Int32VectorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateInt32Vector(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> values = 0) +{ + Int32VectorBuilder builder_(_fbb); + builder_.add_values(values); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateInt32VectorDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *values = nullptr) +{ + auto values__ = values ? _fbb.CreateVector(*values) : 0; + return circle::CreateInt32Vector(_fbb, values__); +} + +flatbuffers::Offset +CreateInt32Vector(flatbuffers::FlatBufferBuilder &_fbb, const Int32VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Uint16VectorT : public flatbuffers::NativeTable +{ + typedef Uint16Vector TableType; + std::vector values{}; +}; + +struct Uint16Vector FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Uint16VectorT NativeTableType; + typedef Uint16VectorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VALUES = 4 + }; + const flatbuffers::Vector *values() const + { + return GetPointer *>(VT_VALUES); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && verifier.EndTable(); + } + Uint16VectorT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Uint16VectorT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Uint16VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Uint16VectorBuilder +{ + typedef Uint16Vector Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_values(flatbuffers::Offset> values) + { + fbb_.AddOffset(Uint16Vector::VT_VALUES, values); + } + explicit Uint16VectorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateUint16Vector(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> values = 0) +{ + Uint16VectorBuilder builder_(_fbb); + builder_.add_values(values); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateUint16VectorDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *values = nullptr) +{ + if (values) + { + _fbb.ForceVectorAlignment(values->size(), sizeof(uint16_t), 4); + } + auto values__ = values ? _fbb.CreateVector(*values) : 0; + return circle::CreateUint16Vector(_fbb, values__); +} + +flatbuffers::Offset +CreateUint16Vector(flatbuffers::FlatBufferBuilder &_fbb, const Uint16VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Uint8VectorT : public flatbuffers::NativeTable +{ + typedef Uint8Vector TableType; + std::vector values{}; +}; + +struct Uint8Vector FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Uint8VectorT NativeTableType; + typedef Uint8VectorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VALUES = 4 + }; + const flatbuffers::Vector *values() const + { + return GetPointer *>(VT_VALUES); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && verifier.EndTable(); + } + Uint8VectorT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Uint8VectorT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Uint8VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Uint8VectorBuilder +{ + typedef Uint8Vector Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_values(flatbuffers::Offset> values) + { + fbb_.AddOffset(Uint8Vector::VT_VALUES, values); + } + explicit Uint8VectorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateUint8Vector(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> values = 0) +{ + Uint8VectorBuilder builder_(_fbb); + builder_.add_values(values); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateUint8VectorDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *values = nullptr) +{ + if (values) + { + _fbb.ForceVectorAlignment(values->size(), sizeof(uint8_t), 4); + } + auto values__ = values ? _fbb.CreateVector(*values) : 0; + return circle::CreateUint8Vector(_fbb, values__); +} + +flatbuffers::Offset +CreateUint8Vector(flatbuffers::FlatBufferBuilder &_fbb, const Uint8VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DimensionMetadataT : public flatbuffers::NativeTable +{ + typedef DimensionMetadata TableType; + circle::DimensionType format = circle::DimensionType_DENSE; + int32_t dense_size = 0; + circle::SparseIndexVectorUnion array_segments{}; + circle::SparseIndexVectorUnion array_indices{}; +}; + +struct DimensionMetadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DimensionMetadataT NativeTableType; + typedef DimensionMetadataBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FORMAT = 4, + VT_DENSE_SIZE = 6, + VT_ARRAY_SEGMENTS_TYPE = 8, + VT_ARRAY_SEGMENTS = 10, + VT_ARRAY_INDICES_TYPE = 12, + VT_ARRAY_INDICES = 14 + }; + circle::DimensionType format() const + { + return static_cast(GetField(VT_FORMAT, 0)); + } + int32_t dense_size() const { return GetField(VT_DENSE_SIZE, 0); } + circle::SparseIndexVector array_segments_type() const + { + return static_cast(GetField(VT_ARRAY_SEGMENTS_TYPE, 0)); + } + const void *array_segments() const { return GetPointer(VT_ARRAY_SEGMENTS); } + template const T *array_segments_as() const; + const circle::Int32Vector *array_segments_as_Int32Vector() const + { + return array_segments_type() == circle::SparseIndexVector_Int32Vector + ? static_cast(array_segments()) + : nullptr; + } + const circle::Uint16Vector *array_segments_as_Uint16Vector() const + { + return array_segments_type() == circle::SparseIndexVector_Uint16Vector + ? static_cast(array_segments()) + : nullptr; + } + const circle::Uint8Vector *array_segments_as_Uint8Vector() const + { + return array_segments_type() == circle::SparseIndexVector_Uint8Vector + ? static_cast(array_segments()) + : nullptr; + } + circle::SparseIndexVector array_indices_type() const + { + return static_cast(GetField(VT_ARRAY_INDICES_TYPE, 0)); + } + const void *array_indices() const { return GetPointer(VT_ARRAY_INDICES); } + template const T *array_indices_as() const; + const circle::Int32Vector *array_indices_as_Int32Vector() const + { + return array_indices_type() == circle::SparseIndexVector_Int32Vector + ? static_cast(array_indices()) + : nullptr; + } + const circle::Uint16Vector *array_indices_as_Uint16Vector() const + { + return array_indices_type() == circle::SparseIndexVector_Uint16Vector + ? static_cast(array_indices()) + : nullptr; + } + const circle::Uint8Vector *array_indices_as_Uint8Vector() const + { + return array_indices_type() == circle::SparseIndexVector_Uint8Vector + ? static_cast(array_indices()) + : nullptr; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_FORMAT) && + VerifyField(verifier, VT_DENSE_SIZE) && + VerifyField(verifier, VT_ARRAY_SEGMENTS_TYPE) && + VerifyOffset(verifier, VT_ARRAY_SEGMENTS) && + VerifySparseIndexVector(verifier, array_segments(), array_segments_type()) && + VerifyField(verifier, VT_ARRAY_INDICES_TYPE) && + VerifyOffset(verifier, VT_ARRAY_INDICES) && + VerifySparseIndexVector(verifier, array_indices(), array_indices_type()) && + verifier.EndTable(); + } + DimensionMetadataT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DimensionMetadataT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DimensionMetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +template <> +inline const circle::Int32Vector *DimensionMetadata::array_segments_as() const +{ + return array_segments_as_Int32Vector(); +} + +template <> +inline const circle::Uint16Vector * +DimensionMetadata::array_segments_as() const +{ + return array_segments_as_Uint16Vector(); +} + +template <> +inline const circle::Uint8Vector *DimensionMetadata::array_segments_as() const +{ + return array_segments_as_Uint8Vector(); +} + +template <> +inline const circle::Int32Vector *DimensionMetadata::array_indices_as() const +{ + return array_indices_as_Int32Vector(); +} + +template <> +inline const circle::Uint16Vector *DimensionMetadata::array_indices_as() const +{ + return array_indices_as_Uint16Vector(); +} + +template <> +inline const circle::Uint8Vector *DimensionMetadata::array_indices_as() const +{ + return array_indices_as_Uint8Vector(); +} + +struct DimensionMetadataBuilder +{ + typedef DimensionMetadata Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_format(circle::DimensionType format) + { + fbb_.AddElement(DimensionMetadata::VT_FORMAT, static_cast(format), 0); + } + void add_dense_size(int32_t dense_size) + { + fbb_.AddElement(DimensionMetadata::VT_DENSE_SIZE, dense_size, 0); + } + void add_array_segments_type(circle::SparseIndexVector array_segments_type) + { + fbb_.AddElement(DimensionMetadata::VT_ARRAY_SEGMENTS_TYPE, + static_cast(array_segments_type), 0); + } + void add_array_segments(flatbuffers::Offset array_segments) + { + fbb_.AddOffset(DimensionMetadata::VT_ARRAY_SEGMENTS, array_segments); + } + void add_array_indices_type(circle::SparseIndexVector array_indices_type) + { + fbb_.AddElement(DimensionMetadata::VT_ARRAY_INDICES_TYPE, + static_cast(array_indices_type), 0); + } + void add_array_indices(flatbuffers::Offset array_indices) + { + fbb_.AddOffset(DimensionMetadata::VT_ARRAY_INDICES, array_indices); + } + explicit DimensionMetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateDimensionMetadata( + flatbuffers::FlatBufferBuilder &_fbb, circle::DimensionType format = circle::DimensionType_DENSE, + int32_t dense_size = 0, + circle::SparseIndexVector array_segments_type = circle::SparseIndexVector_NONE, + flatbuffers::Offset array_segments = 0, + circle::SparseIndexVector array_indices_type = circle::SparseIndexVector_NONE, + flatbuffers::Offset array_indices = 0) +{ + DimensionMetadataBuilder builder_(_fbb); + builder_.add_array_indices(array_indices); + builder_.add_array_segments(array_segments); + builder_.add_dense_size(dense_size); + builder_.add_array_indices_type(array_indices_type); + builder_.add_array_segments_type(array_segments_type); + builder_.add_format(format); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDimensionMetadata(flatbuffers::FlatBufferBuilder &_fbb, const DimensionMetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SparsityParametersT : public flatbuffers::NativeTable +{ + typedef SparsityParameters TableType; + std::vector traversal_order{}; + std::vector block_map{}; + std::vector> dim_metadata{}; +}; + +struct SparsityParameters FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SparsityParametersT NativeTableType; + typedef SparsityParametersBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TRAVERSAL_ORDER = 4, + VT_BLOCK_MAP = 6, + VT_DIM_METADATA = 8 + }; + const flatbuffers::Vector *traversal_order() const + { + return GetPointer *>(VT_TRAVERSAL_ORDER); + } + const flatbuffers::Vector *block_map() const + { + return GetPointer *>(VT_BLOCK_MAP); + } + const flatbuffers::Vector> *dim_metadata() const + { + return GetPointer> *>( + VT_DIM_METADATA); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_TRAVERSAL_ORDER) && + verifier.VerifyVector(traversal_order()) && VerifyOffset(verifier, VT_BLOCK_MAP) && + verifier.VerifyVector(block_map()) && VerifyOffset(verifier, VT_DIM_METADATA) && + verifier.VerifyVector(dim_metadata()) && verifier.VerifyVectorOfTables(dim_metadata()) && + verifier.EndTable(); + } + SparsityParametersT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SparsityParametersT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SparsityParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SparsityParametersBuilder +{ + typedef SparsityParameters Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_traversal_order(flatbuffers::Offset> traversal_order) + { + fbb_.AddOffset(SparsityParameters::VT_TRAVERSAL_ORDER, traversal_order); + } + void add_block_map(flatbuffers::Offset> block_map) + { + fbb_.AddOffset(SparsityParameters::VT_BLOCK_MAP, block_map); + } + void add_dim_metadata( + flatbuffers::Offset>> + dim_metadata) + { + fbb_.AddOffset(SparsityParameters::VT_DIM_METADATA, dim_metadata); + } + explicit SparsityParametersBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSparsityParameters( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> traversal_order = 0, + flatbuffers::Offset> block_map = 0, + flatbuffers::Offset>> + dim_metadata = 0) +{ + SparsityParametersBuilder builder_(_fbb); + builder_.add_dim_metadata(dim_metadata); + builder_.add_block_map(block_map); + builder_.add_traversal_order(traversal_order); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSparsityParametersDirect( + flatbuffers::FlatBufferBuilder &_fbb, const std::vector *traversal_order = nullptr, + const std::vector *block_map = nullptr, + const std::vector> *dim_metadata = nullptr) +{ + auto traversal_order__ = traversal_order ? _fbb.CreateVector(*traversal_order) : 0; + auto block_map__ = block_map ? _fbb.CreateVector(*block_map) : 0; + auto dim_metadata__ = + dim_metadata ? _fbb.CreateVector>(*dim_metadata) + : 0; + return circle::CreateSparsityParameters(_fbb, traversal_order__, block_map__, dim_metadata__); +} + +flatbuffers::Offset +CreateSparsityParameters(flatbuffers::FlatBufferBuilder &_fbb, const SparsityParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TensorT : public flatbuffers::NativeTable +{ + typedef Tensor TableType; + std::vector shape{}; + circle::TensorType type = circle::TensorType_FLOAT32; + uint32_t buffer = 0; + std::string name{}; + std::unique_ptr quantization{}; + bool is_variable = false; + std::unique_ptr sparsity{}; + std::vector shape_signature{}; +}; + +struct Tensor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TensorT NativeTableType; + typedef TensorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_SHAPE = 4, + VT_TYPE = 6, + VT_BUFFER = 8, + VT_NAME = 10, + VT_QUANTIZATION = 12, + VT_IS_VARIABLE = 14, + VT_SPARSITY = 16, + VT_SHAPE_SIGNATURE = 18 + }; + const flatbuffers::Vector *shape() const + { + return GetPointer *>(VT_SHAPE); + } + circle::TensorType type() const + { + return static_cast(GetField(VT_TYPE, 0)); + } + uint32_t buffer() const { return GetField(VT_BUFFER, 0); } + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + const circle::QuantizationParameters *quantization() const + { + return GetPointer(VT_QUANTIZATION); + } + bool is_variable() const { return GetField(VT_IS_VARIABLE, 0) != 0; } + const circle::SparsityParameters *sparsity() const + { + return GetPointer(VT_SPARSITY); + } + const flatbuffers::Vector *shape_signature() const + { + return GetPointer *>(VT_SHAPE_SIGNATURE); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_SHAPE) && + verifier.VerifyVector(shape()) && VerifyField(verifier, VT_TYPE) && + VerifyField(verifier, VT_BUFFER) && VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyOffset(verifier, VT_QUANTIZATION) && + verifier.VerifyTable(quantization()) && VerifyField(verifier, VT_IS_VARIABLE) && + VerifyOffset(verifier, VT_SPARSITY) && verifier.VerifyTable(sparsity()) && + VerifyOffset(verifier, VT_SHAPE_SIGNATURE) && verifier.VerifyVector(shape_signature()) && + verifier.EndTable(); + } + TensorT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TensorT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TensorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TensorBuilder +{ + typedef Tensor Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_shape(flatbuffers::Offset> shape) + { + fbb_.AddOffset(Tensor::VT_SHAPE, shape); + } + void add_type(circle::TensorType type) + { + fbb_.AddElement(Tensor::VT_TYPE, static_cast(type), 0); + } + void add_buffer(uint32_t buffer) { fbb_.AddElement(Tensor::VT_BUFFER, buffer, 0); } + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Tensor::VT_NAME, name); + } + void add_quantization(flatbuffers::Offset quantization) + { + fbb_.AddOffset(Tensor::VT_QUANTIZATION, quantization); + } + void add_is_variable(bool is_variable) + { + fbb_.AddElement(Tensor::VT_IS_VARIABLE, static_cast(is_variable), 0); + } + void add_sparsity(flatbuffers::Offset sparsity) + { + fbb_.AddOffset(Tensor::VT_SPARSITY, sparsity); + } + void add_shape_signature(flatbuffers::Offset> shape_signature) + { + fbb_.AddOffset(Tensor::VT_SHAPE_SIGNATURE, shape_signature); + } + explicit TensorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateTensor(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> shape = 0, + circle::TensorType type = circle::TensorType_FLOAT32, uint32_t buffer = 0, + flatbuffers::Offset name = 0, + flatbuffers::Offset quantization = 0, + bool is_variable = false, flatbuffers::Offset sparsity = 0, + flatbuffers::Offset> shape_signature = 0) +{ + TensorBuilder builder_(_fbb); + builder_.add_shape_signature(shape_signature); + builder_.add_sparsity(sparsity); + builder_.add_quantization(quantization); + builder_.add_name(name); + builder_.add_buffer(buffer); + builder_.add_shape(shape); + builder_.add_is_variable(is_variable); + builder_.add_type(type); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTensorDirect( + flatbuffers::FlatBufferBuilder &_fbb, const std::vector *shape = nullptr, + circle::TensorType type = circle::TensorType_FLOAT32, uint32_t buffer = 0, + const char *name = nullptr, flatbuffers::Offset quantization = 0, + bool is_variable = false, flatbuffers::Offset sparsity = 0, + const std::vector *shape_signature = nullptr) +{ + auto shape__ = shape ? _fbb.CreateVector(*shape) : 0; + auto name__ = name ? _fbb.CreateString(name) : 0; + auto shape_signature__ = shape_signature ? _fbb.CreateVector(*shape_signature) : 0; + return circle::CreateTensor(_fbb, shape__, type, buffer, name__, quantization, is_variable, + sparsity, shape_signature__); +} + +flatbuffers::Offset +CreateTensor(flatbuffers::FlatBufferBuilder &_fbb, const TensorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Conv2DOptionsT : public flatbuffers::NativeTable +{ + typedef Conv2DOptions TableType; + circle::Padding padding = circle::Padding_SAME; + int32_t stride_w = 0; + int32_t stride_h = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + int32_t dilation_w_factor = 1; + int32_t dilation_h_factor = 1; +}; + +struct Conv2DOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Conv2DOptionsT NativeTableType; + typedef Conv2DOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_PADDING = 4, + VT_STRIDE_W = 6, + VT_STRIDE_H = 8, + VT_FUSED_ACTIVATION_FUNCTION = 10, + VT_DILATION_W_FACTOR = 12, + VT_DILATION_H_FACTOR = 14 + }; + circle::Padding padding() const + { + return static_cast(GetField(VT_PADDING, 0)); + } + int32_t stride_w() const { return GetField(VT_STRIDE_W, 0); } + int32_t stride_h() const { return GetField(VT_STRIDE_H, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + int32_t dilation_w_factor() const { return GetField(VT_DILATION_W_FACTOR, 1); } + int32_t dilation_h_factor() const { return GetField(VT_DILATION_H_FACTOR, 1); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_PADDING) && + VerifyField(verifier, VT_STRIDE_W) && + VerifyField(verifier, VT_STRIDE_H) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_DILATION_W_FACTOR) && + VerifyField(verifier, VT_DILATION_H_FACTOR) && verifier.EndTable(); + } + Conv2DOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Conv2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Conv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Conv2DOptionsBuilder +{ + typedef Conv2DOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padding(circle::Padding padding) + { + fbb_.AddElement(Conv2DOptions::VT_PADDING, static_cast(padding), 0); + } + void add_stride_w(int32_t stride_w) + { + fbb_.AddElement(Conv2DOptions::VT_STRIDE_W, stride_w, 0); + } + void add_stride_h(int32_t stride_h) + { + fbb_.AddElement(Conv2DOptions::VT_STRIDE_H, stride_h, 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(Conv2DOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_dilation_w_factor(int32_t dilation_w_factor) + { + fbb_.AddElement(Conv2DOptions::VT_DILATION_W_FACTOR, dilation_w_factor, 1); + } + void add_dilation_h_factor(int32_t dilation_h_factor) + { + fbb_.AddElement(Conv2DOptions::VT_DILATION_H_FACTOR, dilation_h_factor, 1); + } + explicit Conv2DOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConv2DOptions( + flatbuffers::FlatBufferBuilder &_fbb, circle::Padding padding = circle::Padding_SAME, + int32_t stride_w = 0, int32_t stride_h = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + int32_t dilation_w_factor = 1, int32_t dilation_h_factor = 1) +{ + Conv2DOptionsBuilder builder_(_fbb); + builder_.add_dilation_h_factor(dilation_h_factor); + builder_.add_dilation_w_factor(dilation_w_factor); + builder_.add_stride_h(stride_h); + builder_.add_stride_w(stride_w); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_padding(padding); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateConv2DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Conv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Conv3DOptionsT : public flatbuffers::NativeTable +{ + typedef Conv3DOptions TableType; + circle::Padding padding = circle::Padding_SAME; + int32_t stride_d = 0; + int32_t stride_w = 0; + int32_t stride_h = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + int32_t dilation_d_factor = 1; + int32_t dilation_w_factor = 1; + int32_t dilation_h_factor = 1; +}; + +struct Conv3DOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Conv3DOptionsT NativeTableType; + typedef Conv3DOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_PADDING = 4, + VT_STRIDE_D = 6, + VT_STRIDE_W = 8, + VT_STRIDE_H = 10, + VT_FUSED_ACTIVATION_FUNCTION = 12, + VT_DILATION_D_FACTOR = 14, + VT_DILATION_W_FACTOR = 16, + VT_DILATION_H_FACTOR = 18 + }; + circle::Padding padding() const + { + return static_cast(GetField(VT_PADDING, 0)); + } + int32_t stride_d() const { return GetField(VT_STRIDE_D, 0); } + int32_t stride_w() const { return GetField(VT_STRIDE_W, 0); } + int32_t stride_h() const { return GetField(VT_STRIDE_H, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + int32_t dilation_d_factor() const { return GetField(VT_DILATION_D_FACTOR, 1); } + int32_t dilation_w_factor() const { return GetField(VT_DILATION_W_FACTOR, 1); } + int32_t dilation_h_factor() const { return GetField(VT_DILATION_H_FACTOR, 1); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_PADDING) && + VerifyField(verifier, VT_STRIDE_D) && + VerifyField(verifier, VT_STRIDE_W) && + VerifyField(verifier, VT_STRIDE_H) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_DILATION_D_FACTOR) && + VerifyField(verifier, VT_DILATION_W_FACTOR) && + VerifyField(verifier, VT_DILATION_H_FACTOR) && verifier.EndTable(); + } + Conv3DOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Conv3DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Conv3DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Conv3DOptionsBuilder +{ + typedef Conv3DOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padding(circle::Padding padding) + { + fbb_.AddElement(Conv3DOptions::VT_PADDING, static_cast(padding), 0); + } + void add_stride_d(int32_t stride_d) + { + fbb_.AddElement(Conv3DOptions::VT_STRIDE_D, stride_d, 0); + } + void add_stride_w(int32_t stride_w) + { + fbb_.AddElement(Conv3DOptions::VT_STRIDE_W, stride_w, 0); + } + void add_stride_h(int32_t stride_h) + { + fbb_.AddElement(Conv3DOptions::VT_STRIDE_H, stride_h, 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(Conv3DOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_dilation_d_factor(int32_t dilation_d_factor) + { + fbb_.AddElement(Conv3DOptions::VT_DILATION_D_FACTOR, dilation_d_factor, 1); + } + void add_dilation_w_factor(int32_t dilation_w_factor) + { + fbb_.AddElement(Conv3DOptions::VT_DILATION_W_FACTOR, dilation_w_factor, 1); + } + void add_dilation_h_factor(int32_t dilation_h_factor) + { + fbb_.AddElement(Conv3DOptions::VT_DILATION_H_FACTOR, dilation_h_factor, 1); + } + explicit Conv3DOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConv3DOptions( + flatbuffers::FlatBufferBuilder &_fbb, circle::Padding padding = circle::Padding_SAME, + int32_t stride_d = 0, int32_t stride_w = 0, int32_t stride_h = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + int32_t dilation_d_factor = 1, int32_t dilation_w_factor = 1, int32_t dilation_h_factor = 1) +{ + Conv3DOptionsBuilder builder_(_fbb); + builder_.add_dilation_h_factor(dilation_h_factor); + builder_.add_dilation_w_factor(dilation_w_factor); + builder_.add_dilation_d_factor(dilation_d_factor); + builder_.add_stride_h(stride_h); + builder_.add_stride_w(stride_w); + builder_.add_stride_d(stride_d); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_padding(padding); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateConv3DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Conv3DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Pool2DOptionsT : public flatbuffers::NativeTable +{ + typedef Pool2DOptions TableType; + circle::Padding padding = circle::Padding_SAME; + int32_t stride_w = 0; + int32_t stride_h = 0; + int32_t filter_width = 0; + int32_t filter_height = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct Pool2DOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Pool2DOptionsT NativeTableType; + typedef Pool2DOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_PADDING = 4, + VT_STRIDE_W = 6, + VT_STRIDE_H = 8, + VT_FILTER_WIDTH = 10, + VT_FILTER_HEIGHT = 12, + VT_FUSED_ACTIVATION_FUNCTION = 14 + }; + circle::Padding padding() const + { + return static_cast(GetField(VT_PADDING, 0)); + } + int32_t stride_w() const { return GetField(VT_STRIDE_W, 0); } + int32_t stride_h() const { return GetField(VT_STRIDE_H, 0); } + int32_t filter_width() const { return GetField(VT_FILTER_WIDTH, 0); } + int32_t filter_height() const { return GetField(VT_FILTER_HEIGHT, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_PADDING) && + VerifyField(verifier, VT_STRIDE_W) && + VerifyField(verifier, VT_STRIDE_H) && + VerifyField(verifier, VT_FILTER_WIDTH) && + VerifyField(verifier, VT_FILTER_HEIGHT) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + Pool2DOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Pool2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Pool2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Pool2DOptionsBuilder +{ + typedef Pool2DOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padding(circle::Padding padding) + { + fbb_.AddElement(Pool2DOptions::VT_PADDING, static_cast(padding), 0); + } + void add_stride_w(int32_t stride_w) + { + fbb_.AddElement(Pool2DOptions::VT_STRIDE_W, stride_w, 0); + } + void add_stride_h(int32_t stride_h) + { + fbb_.AddElement(Pool2DOptions::VT_STRIDE_H, stride_h, 0); + } + void add_filter_width(int32_t filter_width) + { + fbb_.AddElement(Pool2DOptions::VT_FILTER_WIDTH, filter_width, 0); + } + void add_filter_height(int32_t filter_height) + { + fbb_.AddElement(Pool2DOptions::VT_FILTER_HEIGHT, filter_height, 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(Pool2DOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit Pool2DOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePool2DOptions( + flatbuffers::FlatBufferBuilder &_fbb, circle::Padding padding = circle::Padding_SAME, + int32_t stride_w = 0, int32_t stride_h = 0, int32_t filter_width = 0, int32_t filter_height = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + Pool2DOptionsBuilder builder_(_fbb); + builder_.add_filter_height(filter_height); + builder_.add_filter_width(filter_width); + builder_.add_stride_h(stride_h); + builder_.add_stride_w(stride_w); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_padding(padding); + return builder_.Finish(); +} + +flatbuffers::Offset +CreatePool2DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Pool2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DepthwiseConv2DOptionsT : public flatbuffers::NativeTable +{ + typedef DepthwiseConv2DOptions TableType; + circle::Padding padding = circle::Padding_SAME; + int32_t stride_w = 0; + int32_t stride_h = 0; + int32_t depth_multiplier = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + int32_t dilation_w_factor = 1; + int32_t dilation_h_factor = 1; +}; + +struct DepthwiseConv2DOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DepthwiseConv2DOptionsT NativeTableType; + typedef DepthwiseConv2DOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_PADDING = 4, + VT_STRIDE_W = 6, + VT_STRIDE_H = 8, + VT_DEPTH_MULTIPLIER = 10, + VT_FUSED_ACTIVATION_FUNCTION = 12, + VT_DILATION_W_FACTOR = 14, + VT_DILATION_H_FACTOR = 16 + }; + circle::Padding padding() const + { + return static_cast(GetField(VT_PADDING, 0)); + } + int32_t stride_w() const { return GetField(VT_STRIDE_W, 0); } + int32_t stride_h() const { return GetField(VT_STRIDE_H, 0); } + int32_t depth_multiplier() const { return GetField(VT_DEPTH_MULTIPLIER, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + int32_t dilation_w_factor() const { return GetField(VT_DILATION_W_FACTOR, 1); } + int32_t dilation_h_factor() const { return GetField(VT_DILATION_H_FACTOR, 1); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_PADDING) && + VerifyField(verifier, VT_STRIDE_W) && + VerifyField(verifier, VT_STRIDE_H) && + VerifyField(verifier, VT_DEPTH_MULTIPLIER) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_DILATION_W_FACTOR) && + VerifyField(verifier, VT_DILATION_H_FACTOR) && verifier.EndTable(); + } + DepthwiseConv2DOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DepthwiseConv2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DepthwiseConv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct DepthwiseConv2DOptionsBuilder +{ + typedef DepthwiseConv2DOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padding(circle::Padding padding) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_PADDING, static_cast(padding), 0); + } + void add_stride_w(int32_t stride_w) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_STRIDE_W, stride_w, 0); + } + void add_stride_h(int32_t stride_h) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_STRIDE_H, stride_h, 0); + } + void add_depth_multiplier(int32_t depth_multiplier) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_DEPTH_MULTIPLIER, depth_multiplier, 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_dilation_w_factor(int32_t dilation_w_factor) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_DILATION_W_FACTOR, dilation_w_factor, 1); + } + void add_dilation_h_factor(int32_t dilation_h_factor) + { + fbb_.AddElement(DepthwiseConv2DOptions::VT_DILATION_H_FACTOR, dilation_h_factor, 1); + } + explicit DepthwiseConv2DOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateDepthwiseConv2DOptions( + flatbuffers::FlatBufferBuilder &_fbb, circle::Padding padding = circle::Padding_SAME, + int32_t stride_w = 0, int32_t stride_h = 0, int32_t depth_multiplier = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + int32_t dilation_w_factor = 1, int32_t dilation_h_factor = 1) +{ + DepthwiseConv2DOptionsBuilder builder_(_fbb); + builder_.add_dilation_h_factor(dilation_h_factor); + builder_.add_dilation_w_factor(dilation_w_factor); + builder_.add_depth_multiplier(depth_multiplier); + builder_.add_stride_h(stride_h); + builder_.add_stride_w(stride_w); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_padding(padding); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDepthwiseConv2DOptions(flatbuffers::FlatBufferBuilder &_fbb, + const DepthwiseConv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ConcatEmbeddingsOptionsT : public flatbuffers::NativeTable +{ + typedef ConcatEmbeddingsOptions TableType; + int32_t num_channels = 0; + std::vector num_columns_per_channel{}; + std::vector embedding_dim_per_channel{}; +}; + +struct ConcatEmbeddingsOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ConcatEmbeddingsOptionsT NativeTableType; + typedef ConcatEmbeddingsOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NUM_CHANNELS = 4, + VT_NUM_COLUMNS_PER_CHANNEL = 6, + VT_EMBEDDING_DIM_PER_CHANNEL = 8 + }; + int32_t num_channels() const { return GetField(VT_NUM_CHANNELS, 0); } + const flatbuffers::Vector *num_columns_per_channel() const + { + return GetPointer *>(VT_NUM_COLUMNS_PER_CHANNEL); + } + const flatbuffers::Vector *embedding_dim_per_channel() const + { + return GetPointer *>(VT_EMBEDDING_DIM_PER_CHANNEL); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_NUM_CHANNELS) && + VerifyOffset(verifier, VT_NUM_COLUMNS_PER_CHANNEL) && + verifier.VerifyVector(num_columns_per_channel()) && + VerifyOffset(verifier, VT_EMBEDDING_DIM_PER_CHANNEL) && + verifier.VerifyVector(embedding_dim_per_channel()) && verifier.EndTable(); + } + ConcatEmbeddingsOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ConcatEmbeddingsOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ConcatEmbeddingsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ConcatEmbeddingsOptionsBuilder +{ + typedef ConcatEmbeddingsOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_num_channels(int32_t num_channels) + { + fbb_.AddElement(ConcatEmbeddingsOptions::VT_NUM_CHANNELS, num_channels, 0); + } + void add_num_columns_per_channel( + flatbuffers::Offset> num_columns_per_channel) + { + fbb_.AddOffset(ConcatEmbeddingsOptions::VT_NUM_COLUMNS_PER_CHANNEL, num_columns_per_channel); + } + void add_embedding_dim_per_channel( + flatbuffers::Offset> embedding_dim_per_channel) + { + fbb_.AddOffset(ConcatEmbeddingsOptions::VT_EMBEDDING_DIM_PER_CHANNEL, + embedding_dim_per_channel); + } + explicit ConcatEmbeddingsOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConcatEmbeddingsOptions( + flatbuffers::FlatBufferBuilder &_fbb, int32_t num_channels = 0, + flatbuffers::Offset> num_columns_per_channel = 0, + flatbuffers::Offset> embedding_dim_per_channel = 0) +{ + ConcatEmbeddingsOptionsBuilder builder_(_fbb); + builder_.add_embedding_dim_per_channel(embedding_dim_per_channel); + builder_.add_num_columns_per_channel(num_columns_per_channel); + builder_.add_num_channels(num_channels); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateConcatEmbeddingsOptionsDirect(flatbuffers::FlatBufferBuilder &_fbb, int32_t num_channels = 0, + const std::vector *num_columns_per_channel = nullptr, + const std::vector *embedding_dim_per_channel = nullptr) +{ + auto num_columns_per_channel__ = + num_columns_per_channel ? _fbb.CreateVector(*num_columns_per_channel) : 0; + auto embedding_dim_per_channel__ = + embedding_dim_per_channel ? _fbb.CreateVector(*embedding_dim_per_channel) : 0; + return circle::CreateConcatEmbeddingsOptions(_fbb, num_channels, num_columns_per_channel__, + embedding_dim_per_channel__); +} + +flatbuffers::Offset +CreateConcatEmbeddingsOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ConcatEmbeddingsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LSHProjectionOptionsT : public flatbuffers::NativeTable +{ + typedef LSHProjectionOptions TableType; + circle::LSHProjectionType type = circle::LSHProjectionType_UNKNOWN; +}; + +struct LSHProjectionOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LSHProjectionOptionsT NativeTableType; + typedef LSHProjectionOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TYPE = 4 + }; + circle::LSHProjectionType type() const + { + return static_cast(GetField(VT_TYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_TYPE) && + verifier.EndTable(); + } + LSHProjectionOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LSHProjectionOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LSHProjectionOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LSHProjectionOptionsBuilder +{ + typedef LSHProjectionOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_type(circle::LSHProjectionType type) + { + fbb_.AddElement(LSHProjectionOptions::VT_TYPE, static_cast(type), 0); + } + explicit LSHProjectionOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLSHProjectionOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::LSHProjectionType type = circle::LSHProjectionType_UNKNOWN) +{ + LSHProjectionOptionsBuilder builder_(_fbb); + builder_.add_type(type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLSHProjectionOptions(flatbuffers::FlatBufferBuilder &_fbb, const LSHProjectionOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SVDFOptionsT : public flatbuffers::NativeTable +{ + typedef SVDFOptions TableType; + int32_t rank = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool asymmetric_quantize_inputs = false; +}; + +struct SVDFOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SVDFOptionsT NativeTableType; + typedef SVDFOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_RANK = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 8 + }; + int32_t rank() const { return GetField(VT_RANK, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_RANK) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + SVDFOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SVDFOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SVDFOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SVDFOptionsBuilder +{ + typedef SVDFOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_rank(int32_t rank) { fbb_.AddElement(SVDFOptions::VT_RANK, rank, 0); } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(SVDFOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(SVDFOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit SVDFOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSVDFOptions( + flatbuffers::FlatBufferBuilder &_fbb, int32_t rank = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool asymmetric_quantize_inputs = false) +{ + SVDFOptionsBuilder builder_(_fbb); + builder_.add_rank(rank); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSVDFOptions(flatbuffers::FlatBufferBuilder &_fbb, const SVDFOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct RNNOptionsT : public flatbuffers::NativeTable +{ + typedef RNNOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool asymmetric_quantize_inputs = false; +}; + +struct RNNOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef RNNOptionsT NativeTableType; + typedef RNNOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 6 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + RNNOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(RNNOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const RNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct RNNOptionsBuilder +{ + typedef RNNOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(RNNOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(RNNOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit RNNOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRNNOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool asymmetric_quantize_inputs = false) +{ + RNNOptionsBuilder builder_(_fbb); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, const RNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SequenceRNNOptionsT : public flatbuffers::NativeTable +{ + typedef SequenceRNNOptions TableType; + bool time_major = false; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool asymmetric_quantize_inputs = false; +}; + +struct SequenceRNNOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SequenceRNNOptionsT NativeTableType; + typedef SequenceRNNOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TIME_MAJOR = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 8 + }; + bool time_major() const { return GetField(VT_TIME_MAJOR, 0) != 0; } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_TIME_MAJOR) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + SequenceRNNOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SequenceRNNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SequenceRNNOptionsBuilder +{ + typedef SequenceRNNOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_time_major(bool time_major) + { + fbb_.AddElement(SequenceRNNOptions::VT_TIME_MAJOR, static_cast(time_major), + 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(SequenceRNNOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(SequenceRNNOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit SequenceRNNOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSequenceRNNOptions( + flatbuffers::FlatBufferBuilder &_fbb, bool time_major = false, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool asymmetric_quantize_inputs = false) +{ + SequenceRNNOptionsBuilder builder_(_fbb); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_time_major(time_major); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSequenceRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, const SequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BidirectionalSequenceRNNOptionsT : public flatbuffers::NativeTable +{ + typedef BidirectionalSequenceRNNOptions TableType; + bool time_major = false; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool merge_outputs = false; + bool asymmetric_quantize_inputs = false; +}; + +struct BidirectionalSequenceRNNOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BidirectionalSequenceRNNOptionsT NativeTableType; + typedef BidirectionalSequenceRNNOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TIME_MAJOR = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6, + VT_MERGE_OUTPUTS = 8, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 10 + }; + bool time_major() const { return GetField(VT_TIME_MAJOR, 0) != 0; } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool merge_outputs() const { return GetField(VT_MERGE_OUTPUTS, 0) != 0; } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_TIME_MAJOR) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_MERGE_OUTPUTS) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + BidirectionalSequenceRNNOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BidirectionalSequenceRNNOptionsBuilder +{ + typedef BidirectionalSequenceRNNOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_time_major(bool time_major) + { + fbb_.AddElement(BidirectionalSequenceRNNOptions::VT_TIME_MAJOR, + static_cast(time_major), 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(BidirectionalSequenceRNNOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_merge_outputs(bool merge_outputs) + { + fbb_.AddElement(BidirectionalSequenceRNNOptions::VT_MERGE_OUTPUTS, + static_cast(merge_outputs), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(BidirectionalSequenceRNNOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit BidirectionalSequenceRNNOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBidirectionalSequenceRNNOptions( + flatbuffers::FlatBufferBuilder &_fbb, bool time_major = false, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool merge_outputs = false, bool asymmetric_quantize_inputs = false) +{ + BidirectionalSequenceRNNOptionsBuilder builder_(_fbb); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_merge_outputs(merge_outputs); + builder_.add_fused_activation_function(fused_activation_function); + builder_.add_time_major(time_major); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBidirectionalSequenceRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct FullyConnectedOptionsT : public flatbuffers::NativeTable +{ + typedef FullyConnectedOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + circle::FullyConnectedOptionsWeightsFormat weights_format = + circle::FullyConnectedOptionsWeightsFormat_DEFAULT; + bool keep_num_dims = false; + bool asymmetric_quantize_inputs = false; +}; + +struct FullyConnectedOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FullyConnectedOptionsT NativeTableType; + typedef FullyConnectedOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_WEIGHTS_FORMAT = 6, + VT_KEEP_NUM_DIMS = 8, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 10 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + circle::FullyConnectedOptionsWeightsFormat weights_format() const + { + return static_cast( + GetField(VT_WEIGHTS_FORMAT, 0)); + } + bool keep_num_dims() const { return GetField(VT_KEEP_NUM_DIMS, 0) != 0; } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_WEIGHTS_FORMAT) && + VerifyField(verifier, VT_KEEP_NUM_DIMS) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + FullyConnectedOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FullyConnectedOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const FullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FullyConnectedOptionsBuilder +{ + typedef FullyConnectedOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(FullyConnectedOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_weights_format(circle::FullyConnectedOptionsWeightsFormat weights_format) + { + fbb_.AddElement(FullyConnectedOptions::VT_WEIGHTS_FORMAT, + static_cast(weights_format), 0); + } + void add_keep_num_dims(bool keep_num_dims) + { + fbb_.AddElement(FullyConnectedOptions::VT_KEEP_NUM_DIMS, + static_cast(keep_num_dims), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(FullyConnectedOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit FullyConnectedOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateFullyConnectedOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + circle::FullyConnectedOptionsWeightsFormat weights_format = + circle::FullyConnectedOptionsWeightsFormat_DEFAULT, + bool keep_num_dims = false, bool asymmetric_quantize_inputs = false) +{ + FullyConnectedOptionsBuilder builder_(_fbb); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_keep_num_dims(keep_num_dims); + builder_.add_weights_format(weights_format); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateFullyConnectedOptions(flatbuffers::FlatBufferBuilder &_fbb, const FullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SoftmaxOptionsT : public flatbuffers::NativeTable +{ + typedef SoftmaxOptions TableType; + float beta = 0.0f; +}; + +struct SoftmaxOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SoftmaxOptionsT NativeTableType; + typedef SoftmaxOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_BETA = 4 + }; + float beta() const { return GetField(VT_BETA, 0.0f); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_BETA) && + verifier.EndTable(); + } + SoftmaxOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SoftmaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SoftmaxOptionsBuilder +{ + typedef SoftmaxOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_beta(float beta) { fbb_.AddElement(SoftmaxOptions::VT_BETA, beta, 0.0f); } + explicit SoftmaxOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb, float beta = 0.0f) +{ + SoftmaxOptionsBuilder builder_(_fbb); + builder_.add_beta(beta); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const SoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ConcatenationOptionsT : public flatbuffers::NativeTable +{ + typedef ConcatenationOptions TableType; + int32_t axis = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct ConcatenationOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ConcatenationOptionsT NativeTableType; + typedef ConcatenationOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_AXIS = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6 + }; + int32_t axis() const { return GetField(VT_AXIS, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_AXIS) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + ConcatenationOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ConcatenationOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ConcatenationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ConcatenationOptionsBuilder +{ + typedef ConcatenationOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_axis(int32_t axis) { fbb_.AddElement(ConcatenationOptions::VT_AXIS, axis, 0); } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(ConcatenationOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit ConcatenationOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConcatenationOptions( + flatbuffers::FlatBufferBuilder &_fbb, int32_t axis = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + ConcatenationOptionsBuilder builder_(_fbb); + builder_.add_axis(axis); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateConcatenationOptions(flatbuffers::FlatBufferBuilder &_fbb, const ConcatenationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct AddOptionsT : public flatbuffers::NativeTable +{ + typedef AddOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool pot_scale_int16 = true; +}; + +struct AddOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef AddOptionsT NativeTableType; + typedef AddOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_POT_SCALE_INT16 = 6 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool pot_scale_int16() const { return GetField(VT_POT_SCALE_INT16, 1) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_POT_SCALE_INT16) && verifier.EndTable(); + } + AddOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AddOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const AddOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AddOptionsBuilder +{ + typedef AddOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(AddOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_pot_scale_int16(bool pot_scale_int16) + { + fbb_.AddElement(AddOptions::VT_POT_SCALE_INT16, static_cast(pot_scale_int16), + 1); + } + explicit AddOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAddOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool pot_scale_int16 = true) +{ + AddOptionsBuilder builder_(_fbb); + builder_.add_pot_scale_int16(pot_scale_int16); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateAddOptions(flatbuffers::FlatBufferBuilder &_fbb, const AddOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MulOptionsT : public flatbuffers::NativeTable +{ + typedef MulOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct MulOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MulOptionsT NativeTableType; + typedef MulOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + MulOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MulOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MulOptionsBuilder +{ + typedef MulOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(MulOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit MulOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateMulOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + MulOptionsBuilder builder_(_fbb); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateMulOptions(flatbuffers::FlatBufferBuilder &_fbb, const MulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct L2NormOptionsT : public flatbuffers::NativeTable +{ + typedef L2NormOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct L2NormOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef L2NormOptionsT NativeTableType; + typedef L2NormOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + L2NormOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(L2NormOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const L2NormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct L2NormOptionsBuilder +{ + typedef L2NormOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(L2NormOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit L2NormOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateL2NormOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + L2NormOptionsBuilder builder_(_fbb); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateL2NormOptions(flatbuffers::FlatBufferBuilder &_fbb, const L2NormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LocalResponseNormalizationOptionsT : public flatbuffers::NativeTable +{ + typedef LocalResponseNormalizationOptions TableType; + int32_t radius = 0; + float bias = 0.0f; + float alpha = 0.0f; + float beta = 0.0f; +}; + +struct LocalResponseNormalizationOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LocalResponseNormalizationOptionsT NativeTableType; + typedef LocalResponseNormalizationOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_RADIUS = 4, + VT_BIAS = 6, + VT_ALPHA = 8, + VT_BETA = 10 + }; + int32_t radius() const { return GetField(VT_RADIUS, 0); } + float bias() const { return GetField(VT_BIAS, 0.0f); } + float alpha() const { return GetField(VT_ALPHA, 0.0f); } + float beta() const { return GetField(VT_BETA, 0.0f); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_RADIUS) && + VerifyField(verifier, VT_BIAS) && VerifyField(verifier, VT_ALPHA) && + VerifyField(verifier, VT_BETA) && verifier.EndTable(); + } + LocalResponseNormalizationOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LocalResponseNormalizationOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LocalResponseNormalizationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LocalResponseNormalizationOptionsBuilder +{ + typedef LocalResponseNormalizationOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_radius(int32_t radius) + { + fbb_.AddElement(LocalResponseNormalizationOptions::VT_RADIUS, radius, 0); + } + void add_bias(float bias) + { + fbb_.AddElement(LocalResponseNormalizationOptions::VT_BIAS, bias, 0.0f); + } + void add_alpha(float alpha) + { + fbb_.AddElement(LocalResponseNormalizationOptions::VT_ALPHA, alpha, 0.0f); + } + void add_beta(float beta) + { + fbb_.AddElement(LocalResponseNormalizationOptions::VT_BETA, beta, 0.0f); + } + explicit LocalResponseNormalizationOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLocalResponseNormalizationOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t radius = 0, + float bias = 0.0f, float alpha = 0.0f, float beta = 0.0f) +{ + LocalResponseNormalizationOptionsBuilder builder_(_fbb); + builder_.add_beta(beta); + builder_.add_alpha(alpha); + builder_.add_bias(bias); + builder_.add_radius(radius); + return builder_.Finish(); +} + +flatbuffers::Offset CreateLocalResponseNormalizationOptions( + flatbuffers::FlatBufferBuilder &_fbb, const LocalResponseNormalizationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LSTMOptionsT : public flatbuffers::NativeTable +{ + typedef LSTMOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + float cell_clip = 0.0f; + float proj_clip = 0.0f; + circle::LSTMKernelType kernel_type = circle::LSTMKernelType_FULL; + bool asymmetric_quantize_inputs = false; +}; + +struct LSTMOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LSTMOptionsT NativeTableType; + typedef LSTMOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_CELL_CLIP = 6, + VT_PROJ_CLIP = 8, + VT_KERNEL_TYPE = 10, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 12 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + float cell_clip() const { return GetField(VT_CELL_CLIP, 0.0f); } + float proj_clip() const { return GetField(VT_PROJ_CLIP, 0.0f); } + circle::LSTMKernelType kernel_type() const + { + return static_cast(GetField(VT_KERNEL_TYPE, 0)); + } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_CELL_CLIP) && + VerifyField(verifier, VT_PROJ_CLIP) && + VerifyField(verifier, VT_KERNEL_TYPE) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + LSTMOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LSTMOptionsBuilder +{ + typedef LSTMOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(LSTMOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_cell_clip(float cell_clip) + { + fbb_.AddElement(LSTMOptions::VT_CELL_CLIP, cell_clip, 0.0f); + } + void add_proj_clip(float proj_clip) + { + fbb_.AddElement(LSTMOptions::VT_PROJ_CLIP, proj_clip, 0.0f); + } + void add_kernel_type(circle::LSTMKernelType kernel_type) + { + fbb_.AddElement(LSTMOptions::VT_KERNEL_TYPE, static_cast(kernel_type), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(LSTMOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit LSTMOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateLSTMOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + float cell_clip = 0.0f, float proj_clip = 0.0f, + circle::LSTMKernelType kernel_type = circle::LSTMKernelType_FULL, + bool asymmetric_quantize_inputs = false) +{ + LSTMOptionsBuilder builder_(_fbb); + builder_.add_proj_clip(proj_clip); + builder_.add_cell_clip(cell_clip); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_kernel_type(kernel_type); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLSTMOptions(flatbuffers::FlatBufferBuilder &_fbb, const LSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct UnidirectionalSequenceLSTMOptionsT : public flatbuffers::NativeTable +{ + typedef UnidirectionalSequenceLSTMOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + float cell_clip = 0.0f; + float proj_clip = 0.0f; + bool time_major = false; + bool asymmetric_quantize_inputs = false; +}; + +struct UnidirectionalSequenceLSTMOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef UnidirectionalSequenceLSTMOptionsT NativeTableType; + typedef UnidirectionalSequenceLSTMOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_CELL_CLIP = 6, + VT_PROJ_CLIP = 8, + VT_TIME_MAJOR = 10, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 12 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + float cell_clip() const { return GetField(VT_CELL_CLIP, 0.0f); } + float proj_clip() const { return GetField(VT_PROJ_CLIP, 0.0f); } + bool time_major() const { return GetField(VT_TIME_MAJOR, 0) != 0; } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_CELL_CLIP) && + VerifyField(verifier, VT_PROJ_CLIP) && + VerifyField(verifier, VT_TIME_MAJOR) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + UnidirectionalSequenceLSTMOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct UnidirectionalSequenceLSTMOptionsBuilder +{ + typedef UnidirectionalSequenceLSTMOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(UnidirectionalSequenceLSTMOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_cell_clip(float cell_clip) + { + fbb_.AddElement(UnidirectionalSequenceLSTMOptions::VT_CELL_CLIP, cell_clip, 0.0f); + } + void add_proj_clip(float proj_clip) + { + fbb_.AddElement(UnidirectionalSequenceLSTMOptions::VT_PROJ_CLIP, proj_clip, 0.0f); + } + void add_time_major(bool time_major) + { + fbb_.AddElement(UnidirectionalSequenceLSTMOptions::VT_TIME_MAJOR, + static_cast(time_major), 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(UnidirectionalSequenceLSTMOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit UnidirectionalSequenceLSTMOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateUnidirectionalSequenceLSTMOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + float cell_clip = 0.0f, float proj_clip = 0.0f, bool time_major = false, + bool asymmetric_quantize_inputs = false) +{ + UnidirectionalSequenceLSTMOptionsBuilder builder_(_fbb); + builder_.add_proj_clip(proj_clip); + builder_.add_cell_clip(cell_clip); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_time_major(time_major); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset CreateUnidirectionalSequenceLSTMOptions( + flatbuffers::FlatBufferBuilder &_fbb, const UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BidirectionalSequenceLSTMOptionsT : public flatbuffers::NativeTable +{ + typedef BidirectionalSequenceLSTMOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + float cell_clip = 0.0f; + float proj_clip = 0.0f; + bool merge_outputs = false; + bool time_major = true; + bool asymmetric_quantize_inputs = false; +}; + +struct BidirectionalSequenceLSTMOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BidirectionalSequenceLSTMOptionsT NativeTableType; + typedef BidirectionalSequenceLSTMOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_CELL_CLIP = 6, + VT_PROJ_CLIP = 8, + VT_MERGE_OUTPUTS = 10, + VT_TIME_MAJOR = 12, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 14 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + float cell_clip() const { return GetField(VT_CELL_CLIP, 0.0f); } + float proj_clip() const { return GetField(VT_PROJ_CLIP, 0.0f); } + bool merge_outputs() const { return GetField(VT_MERGE_OUTPUTS, 0) != 0; } + bool time_major() const { return GetField(VT_TIME_MAJOR, 1) != 0; } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_CELL_CLIP) && + VerifyField(verifier, VT_PROJ_CLIP) && + VerifyField(verifier, VT_MERGE_OUTPUTS) && + VerifyField(verifier, VT_TIME_MAJOR) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + BidirectionalSequenceLSTMOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BidirectionalSequenceLSTMOptionsBuilder +{ + typedef BidirectionalSequenceLSTMOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_cell_clip(float cell_clip) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_CELL_CLIP, cell_clip, 0.0f); + } + void add_proj_clip(float proj_clip) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_PROJ_CLIP, proj_clip, 0.0f); + } + void add_merge_outputs(bool merge_outputs) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_MERGE_OUTPUTS, + static_cast(merge_outputs), 0); + } + void add_time_major(bool time_major) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_TIME_MAJOR, + static_cast(time_major), 1); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(BidirectionalSequenceLSTMOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit BidirectionalSequenceLSTMOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBidirectionalSequenceLSTMOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + float cell_clip = 0.0f, float proj_clip = 0.0f, bool merge_outputs = false, + bool time_major = true, bool asymmetric_quantize_inputs = false) +{ + BidirectionalSequenceLSTMOptionsBuilder builder_(_fbb); + builder_.add_proj_clip(proj_clip); + builder_.add_cell_clip(cell_clip); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_time_major(time_major); + builder_.add_merge_outputs(merge_outputs); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ResizeBilinearOptionsT : public flatbuffers::NativeTable +{ + typedef ResizeBilinearOptions TableType; + bool align_corners = false; + bool half_pixel_centers = false; +}; + +struct ResizeBilinearOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ResizeBilinearOptionsT NativeTableType; + typedef ResizeBilinearOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_ALIGN_CORNERS = 8, + VT_HALF_PIXEL_CENTERS = 10 + }; + bool align_corners() const { return GetField(VT_ALIGN_CORNERS, 0) != 0; } + bool half_pixel_centers() const { return GetField(VT_HALF_PIXEL_CENTERS, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_ALIGN_CORNERS) && + VerifyField(verifier, VT_HALF_PIXEL_CENTERS) && verifier.EndTable(); + } + ResizeBilinearOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ResizeBilinearOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ResizeBilinearOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ResizeBilinearOptionsBuilder +{ + typedef ResizeBilinearOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_align_corners(bool align_corners) + { + fbb_.AddElement(ResizeBilinearOptions::VT_ALIGN_CORNERS, + static_cast(align_corners), 0); + } + void add_half_pixel_centers(bool half_pixel_centers) + { + fbb_.AddElement(ResizeBilinearOptions::VT_HALF_PIXEL_CENTERS, + static_cast(half_pixel_centers), 0); + } + explicit ResizeBilinearOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateResizeBilinearOptions(flatbuffers::FlatBufferBuilder &_fbb, bool align_corners = false, + bool half_pixel_centers = false) +{ + ResizeBilinearOptionsBuilder builder_(_fbb); + builder_.add_half_pixel_centers(half_pixel_centers); + builder_.add_align_corners(align_corners); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateResizeBilinearOptions(flatbuffers::FlatBufferBuilder &_fbb, const ResizeBilinearOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ResizeNearestNeighborOptionsT : public flatbuffers::NativeTable +{ + typedef ResizeNearestNeighborOptions TableType; + bool align_corners = false; + bool half_pixel_centers = false; +}; + +struct ResizeNearestNeighborOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ResizeNearestNeighborOptionsT NativeTableType; + typedef ResizeNearestNeighborOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_ALIGN_CORNERS = 4, + VT_HALF_PIXEL_CENTERS = 6 + }; + bool align_corners() const { return GetField(VT_ALIGN_CORNERS, 0) != 0; } + bool half_pixel_centers() const { return GetField(VT_HALF_PIXEL_CENTERS, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_ALIGN_CORNERS) && + VerifyField(verifier, VT_HALF_PIXEL_CENTERS) && verifier.EndTable(); + } + ResizeNearestNeighborOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ResizeNearestNeighborOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ResizeNearestNeighborOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ResizeNearestNeighborOptionsBuilder +{ + typedef ResizeNearestNeighborOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_align_corners(bool align_corners) + { + fbb_.AddElement(ResizeNearestNeighborOptions::VT_ALIGN_CORNERS, + static_cast(align_corners), 0); + } + void add_half_pixel_centers(bool half_pixel_centers) + { + fbb_.AddElement(ResizeNearestNeighborOptions::VT_HALF_PIXEL_CENTERS, + static_cast(half_pixel_centers), 0); + } + explicit ResizeNearestNeighborOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateResizeNearestNeighborOptions(flatbuffers::FlatBufferBuilder &_fbb, bool align_corners = false, + bool half_pixel_centers = false) +{ + ResizeNearestNeighborOptionsBuilder builder_(_fbb); + builder_.add_half_pixel_centers(half_pixel_centers); + builder_.add_align_corners(align_corners); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateResizeNearestNeighborOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ResizeNearestNeighborOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CallOptionsT : public flatbuffers::NativeTable +{ + typedef CallOptions TableType; + uint32_t subgraph = 0; +}; + +struct CallOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CallOptionsT NativeTableType; + typedef CallOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_SUBGRAPH = 4 + }; + uint32_t subgraph() const { return GetField(VT_SUBGRAPH, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_SUBGRAPH) && + verifier.EndTable(); + } + CallOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CallOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CallOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CallOptionsBuilder +{ + typedef CallOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_subgraph(uint32_t subgraph) + { + fbb_.AddElement(CallOptions::VT_SUBGRAPH, subgraph, 0); + } + explicit CallOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateCallOptions(flatbuffers::FlatBufferBuilder &_fbb, + uint32_t subgraph = 0) +{ + CallOptionsBuilder builder_(_fbb); + builder_.add_subgraph(subgraph); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateCallOptions(flatbuffers::FlatBufferBuilder &_fbb, const CallOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct PadOptionsT : public flatbuffers::NativeTable +{ + typedef PadOptions TableType; +}; + +struct PadOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef PadOptionsT NativeTableType; + typedef PadOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + PadOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PadOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const PadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PadOptionsBuilder +{ + typedef PadOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PadOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePadOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + PadOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreatePadOptions(flatbuffers::FlatBufferBuilder &_fbb, const PadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct PadV2OptionsT : public flatbuffers::NativeTable +{ + typedef PadV2Options TableType; +}; + +struct PadV2Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef PadV2OptionsT NativeTableType; + typedef PadV2OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + PadV2OptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PadV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const PadV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PadV2OptionsBuilder +{ + typedef PadV2Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PadV2OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePadV2Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + PadV2OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreatePadV2Options(flatbuffers::FlatBufferBuilder &_fbb, const PadV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ReshapeOptionsT : public flatbuffers::NativeTable +{ + typedef ReshapeOptions TableType; + std::vector new_shape{}; +}; + +struct ReshapeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ReshapeOptionsT NativeTableType; + typedef ReshapeOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NEW_SHAPE = 4 + }; + const flatbuffers::Vector *new_shape() const + { + return GetPointer *>(VT_NEW_SHAPE); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NEW_SHAPE) && + verifier.VerifyVector(new_shape()) && verifier.EndTable(); + } + ReshapeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ReshapeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReshapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ReshapeOptionsBuilder +{ + typedef ReshapeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_new_shape(flatbuffers::Offset> new_shape) + { + fbb_.AddOffset(ReshapeOptions::VT_NEW_SHAPE, new_shape); + } + explicit ReshapeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateReshapeOptions(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> new_shape = 0) +{ + ReshapeOptionsBuilder builder_(_fbb); + builder_.add_new_shape(new_shape); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateReshapeOptionsDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *new_shape = nullptr) +{ + auto new_shape__ = new_shape ? _fbb.CreateVector(*new_shape) : 0; + return circle::CreateReshapeOptions(_fbb, new_shape__); +} + +flatbuffers::Offset +CreateReshapeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReshapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SpaceToBatchNDOptionsT : public flatbuffers::NativeTable +{ + typedef SpaceToBatchNDOptions TableType; +}; + +struct SpaceToBatchNDOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SpaceToBatchNDOptionsT NativeTableType; + typedef SpaceToBatchNDOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SpaceToBatchNDOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SpaceToBatchNDOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToBatchNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SpaceToBatchNDOptionsBuilder +{ + typedef SpaceToBatchNDOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SpaceToBatchNDOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSpaceToBatchNDOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SpaceToBatchNDOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSpaceToBatchNDOptions(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToBatchNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BatchToSpaceNDOptionsT : public flatbuffers::NativeTable +{ + typedef BatchToSpaceNDOptions TableType; +}; + +struct BatchToSpaceNDOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BatchToSpaceNDOptionsT NativeTableType; + typedef BatchToSpaceNDOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + BatchToSpaceNDOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BatchToSpaceNDOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BatchToSpaceNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BatchToSpaceNDOptionsBuilder +{ + typedef BatchToSpaceNDOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit BatchToSpaceNDOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateBatchToSpaceNDOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + BatchToSpaceNDOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBatchToSpaceNDOptions(flatbuffers::FlatBufferBuilder &_fbb, const BatchToSpaceNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SkipGramOptionsT : public flatbuffers::NativeTable +{ + typedef SkipGramOptions TableType; + int32_t ngram_size = 0; + int32_t max_skip_size = 0; + bool include_all_ngrams = false; +}; + +struct SkipGramOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SkipGramOptionsT NativeTableType; + typedef SkipGramOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NGRAM_SIZE = 4, + VT_MAX_SKIP_SIZE = 6, + VT_INCLUDE_ALL_NGRAMS = 8 + }; + int32_t ngram_size() const { return GetField(VT_NGRAM_SIZE, 0); } + int32_t max_skip_size() const { return GetField(VT_MAX_SKIP_SIZE, 0); } + bool include_all_ngrams() const { return GetField(VT_INCLUDE_ALL_NGRAMS, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_NGRAM_SIZE) && + VerifyField(verifier, VT_MAX_SKIP_SIZE) && + VerifyField(verifier, VT_INCLUDE_ALL_NGRAMS) && verifier.EndTable(); + } + SkipGramOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SkipGramOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SkipGramOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SkipGramOptionsBuilder +{ + typedef SkipGramOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_ngram_size(int32_t ngram_size) + { + fbb_.AddElement(SkipGramOptions::VT_NGRAM_SIZE, ngram_size, 0); + } + void add_max_skip_size(int32_t max_skip_size) + { + fbb_.AddElement(SkipGramOptions::VT_MAX_SKIP_SIZE, max_skip_size, 0); + } + void add_include_all_ngrams(bool include_all_ngrams) + { + fbb_.AddElement(SkipGramOptions::VT_INCLUDE_ALL_NGRAMS, + static_cast(include_all_ngrams), 0); + } + explicit SkipGramOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSkipGramOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t ngram_size = 0, + int32_t max_skip_size = 0, bool include_all_ngrams = false) +{ + SkipGramOptionsBuilder builder_(_fbb); + builder_.add_max_skip_size(max_skip_size); + builder_.add_ngram_size(ngram_size); + builder_.add_include_all_ngrams(include_all_ngrams); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSkipGramOptions(flatbuffers::FlatBufferBuilder &_fbb, const SkipGramOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SpaceToDepthOptionsT : public flatbuffers::NativeTable +{ + typedef SpaceToDepthOptions TableType; + int32_t block_size = 0; +}; + +struct SpaceToDepthOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SpaceToDepthOptionsT NativeTableType; + typedef SpaceToDepthOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_BLOCK_SIZE = 4 + }; + int32_t block_size() const { return GetField(VT_BLOCK_SIZE, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_BLOCK_SIZE) && + verifier.EndTable(); + } + SpaceToDepthOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SpaceToDepthOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToDepthOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SpaceToDepthOptionsBuilder +{ + typedef SpaceToDepthOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_block_size(int32_t block_size) + { + fbb_.AddElement(SpaceToDepthOptions::VT_BLOCK_SIZE, block_size, 0); + } + explicit SpaceToDepthOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSpaceToDepthOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t block_size = 0) +{ + SpaceToDepthOptionsBuilder builder_(_fbb); + builder_.add_block_size(block_size); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSpaceToDepthOptions(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToDepthOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DepthToSpaceOptionsT : public flatbuffers::NativeTable +{ + typedef DepthToSpaceOptions TableType; + int32_t block_size = 0; +}; + +struct DepthToSpaceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DepthToSpaceOptionsT NativeTableType; + typedef DepthToSpaceOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_BLOCK_SIZE = 4 + }; + int32_t block_size() const { return GetField(VT_BLOCK_SIZE, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_BLOCK_SIZE) && + verifier.EndTable(); + } + DepthToSpaceOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DepthToSpaceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DepthToSpaceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct DepthToSpaceOptionsBuilder +{ + typedef DepthToSpaceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_block_size(int32_t block_size) + { + fbb_.AddElement(DepthToSpaceOptions::VT_BLOCK_SIZE, block_size, 0); + } + explicit DepthToSpaceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateDepthToSpaceOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t block_size = 0) +{ + DepthToSpaceOptionsBuilder builder_(_fbb); + builder_.add_block_size(block_size); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDepthToSpaceOptions(flatbuffers::FlatBufferBuilder &_fbb, const DepthToSpaceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SubOptionsT : public flatbuffers::NativeTable +{ + typedef SubOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; + bool pot_scale_int16 = true; +}; + +struct SubOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SubOptionsT NativeTableType; + typedef SubOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4, + VT_POT_SCALE_INT16 = 6 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool pot_scale_int16() const { return GetField(VT_POT_SCALE_INT16, 1) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && + VerifyField(verifier, VT_POT_SCALE_INT16) && verifier.EndTable(); + } + SubOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SubOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SubOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SubOptionsBuilder +{ + typedef SubOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(SubOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + void add_pot_scale_int16(bool pot_scale_int16) + { + fbb_.AddElement(SubOptions::VT_POT_SCALE_INT16, static_cast(pot_scale_int16), + 1); + } + explicit SubOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSubOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE, + bool pot_scale_int16 = true) +{ + SubOptionsBuilder builder_(_fbb); + builder_.add_pot_scale_int16(pot_scale_int16); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSubOptions(flatbuffers::FlatBufferBuilder &_fbb, const SubOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DivOptionsT : public flatbuffers::NativeTable +{ + typedef DivOptions TableType; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct DivOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DivOptionsT NativeTableType; + typedef DivOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_FUSED_ACTIVATION_FUNCTION = 4 + }; + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + DivOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DivOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct DivOptionsBuilder +{ + typedef DivOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(DivOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit DivOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateDivOptions( + flatbuffers::FlatBufferBuilder &_fbb, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + DivOptionsBuilder builder_(_fbb); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDivOptions(flatbuffers::FlatBufferBuilder &_fbb, const DivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TopKV2OptionsT : public flatbuffers::NativeTable +{ + typedef TopKV2Options TableType; +}; + +struct TopKV2Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TopKV2OptionsT NativeTableType; + typedef TopKV2OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + TopKV2OptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TopKV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TopKV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TopKV2OptionsBuilder +{ + typedef TopKV2Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit TopKV2OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTopKV2Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + TopKV2OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateTopKV2Options(flatbuffers::FlatBufferBuilder &_fbb, const TopKV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct EmbeddingLookupSparseOptionsT : public flatbuffers::NativeTable +{ + typedef EmbeddingLookupSparseOptions TableType; + circle::CombinerType combiner = circle::CombinerType_SUM; +}; + +struct EmbeddingLookupSparseOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef EmbeddingLookupSparseOptionsT NativeTableType; + typedef EmbeddingLookupSparseOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_COMBINER = 4 + }; + circle::CombinerType combiner() const + { + return static_cast(GetField(VT_COMBINER, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_COMBINER) && + verifier.EndTable(); + } + EmbeddingLookupSparseOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct EmbeddingLookupSparseOptionsBuilder +{ + typedef EmbeddingLookupSparseOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_combiner(circle::CombinerType combiner) + { + fbb_.AddElement(EmbeddingLookupSparseOptions::VT_COMBINER, + static_cast(combiner), 0); + } + explicit EmbeddingLookupSparseOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateEmbeddingLookupSparseOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::CombinerType combiner = circle::CombinerType_SUM) +{ + EmbeddingLookupSparseOptionsBuilder builder_(_fbb); + builder_.add_combiner(combiner); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateEmbeddingLookupSparseOptions(flatbuffers::FlatBufferBuilder &_fbb, + const EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct GatherOptionsT : public flatbuffers::NativeTable +{ + typedef GatherOptions TableType; + int32_t axis = 0; + int32_t batch_dims = 0; +}; + +struct GatherOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef GatherOptionsT NativeTableType; + typedef GatherOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_AXIS = 4, + VT_BATCH_DIMS = 6 + }; + int32_t axis() const { return GetField(VT_AXIS, 0); } + int32_t batch_dims() const { return GetField(VT_BATCH_DIMS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_AXIS) && + VerifyField(verifier, VT_BATCH_DIMS) && verifier.EndTable(); + } + GatherOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(GatherOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const GatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct GatherOptionsBuilder +{ + typedef GatherOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_axis(int32_t axis) { fbb_.AddElement(GatherOptions::VT_AXIS, axis, 0); } + void add_batch_dims(int32_t batch_dims) + { + fbb_.AddElement(GatherOptions::VT_BATCH_DIMS, batch_dims, 0); + } + explicit GatherOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t axis = 0, int32_t batch_dims = 0) +{ + GatherOptionsBuilder builder_(_fbb); + builder_.add_batch_dims(batch_dims); + builder_.add_axis(axis); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, const GatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TransposeOptionsT : public flatbuffers::NativeTable +{ + typedef TransposeOptions TableType; +}; + +struct TransposeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TransposeOptionsT NativeTableType; + typedef TransposeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + TransposeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TransposeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TransposeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TransposeOptionsBuilder +{ + typedef TransposeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit TransposeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateTransposeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + TransposeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateTransposeOptions(flatbuffers::FlatBufferBuilder &_fbb, const TransposeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ExpOptionsT : public flatbuffers::NativeTable +{ + typedef ExpOptions TableType; +}; + +struct ExpOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ExpOptionsT NativeTableType; + typedef ExpOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ExpOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ExpOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ExpOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ExpOptionsBuilder +{ + typedef ExpOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ExpOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateExpOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + ExpOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateExpOptions(flatbuffers::FlatBufferBuilder &_fbb, const ExpOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CosOptionsT : public flatbuffers::NativeTable +{ + typedef CosOptions TableType; +}; + +struct CosOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CosOptionsT NativeTableType; + typedef CosOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + CosOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CosOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CosOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CosOptionsBuilder +{ + typedef CosOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit CosOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateCosOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + CosOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateCosOptions(flatbuffers::FlatBufferBuilder &_fbb, const CosOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ReducerOptionsT : public flatbuffers::NativeTable +{ + typedef ReducerOptions TableType; + bool keep_dims = false; +}; + +struct ReducerOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ReducerOptionsT NativeTableType; + typedef ReducerOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_KEEP_DIMS = 4 + }; + bool keep_dims() const { return GetField(VT_KEEP_DIMS, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_KEEP_DIMS) && + verifier.EndTable(); + } + ReducerOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ReducerOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReducerOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ReducerOptionsBuilder +{ + typedef ReducerOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_keep_dims(bool keep_dims) + { + fbb_.AddElement(ReducerOptions::VT_KEEP_DIMS, static_cast(keep_dims), 0); + } + explicit ReducerOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateReducerOptions(flatbuffers::FlatBufferBuilder &_fbb, bool keep_dims = false) +{ + ReducerOptionsBuilder builder_(_fbb); + builder_.add_keep_dims(keep_dims); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateReducerOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReducerOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SqueezeOptionsT : public flatbuffers::NativeTable +{ + typedef SqueezeOptions TableType; + std::vector squeeze_dims{}; +}; + +struct SqueezeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SqueezeOptionsT NativeTableType; + typedef SqueezeOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_SQUEEZE_DIMS = 4 + }; + const flatbuffers::Vector *squeeze_dims() const + { + return GetPointer *>(VT_SQUEEZE_DIMS); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_SQUEEZE_DIMS) && + verifier.VerifyVector(squeeze_dims()) && verifier.EndTable(); + } + SqueezeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SqueezeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SqueezeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SqueezeOptionsBuilder +{ + typedef SqueezeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_squeeze_dims(flatbuffers::Offset> squeeze_dims) + { + fbb_.AddOffset(SqueezeOptions::VT_SQUEEZE_DIMS, squeeze_dims); + } + explicit SqueezeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSqueezeOptions(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> squeeze_dims = 0) +{ + SqueezeOptionsBuilder builder_(_fbb); + builder_.add_squeeze_dims(squeeze_dims); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateSqueezeOptionsDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *squeeze_dims = nullptr) +{ + auto squeeze_dims__ = squeeze_dims ? _fbb.CreateVector(*squeeze_dims) : 0; + return circle::CreateSqueezeOptions(_fbb, squeeze_dims__); +} + +flatbuffers::Offset +CreateSqueezeOptions(flatbuffers::FlatBufferBuilder &_fbb, const SqueezeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SplitOptionsT : public flatbuffers::NativeTable +{ + typedef SplitOptions TableType; + int32_t num_splits = 0; +}; + +struct SplitOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SplitOptionsT NativeTableType; + typedef SplitOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NUM_SPLITS = 4 + }; + int32_t num_splits() const { return GetField(VT_NUM_SPLITS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_NUM_SPLITS) && + verifier.EndTable(); + } + SplitOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SplitOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SplitOptionsBuilder +{ + typedef SplitOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_num_splits(int32_t num_splits) + { + fbb_.AddElement(SplitOptions::VT_NUM_SPLITS, num_splits, 0); + } + explicit SplitOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSplitOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t num_splits = 0) +{ + SplitOptionsBuilder builder_(_fbb); + builder_.add_num_splits(num_splits); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSplitOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SplitVOptionsT : public flatbuffers::NativeTable +{ + typedef SplitVOptions TableType; + int32_t num_splits = 0; +}; + +struct SplitVOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SplitVOptionsT NativeTableType; + typedef SplitVOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NUM_SPLITS = 4 + }; + int32_t num_splits() const { return GetField(VT_NUM_SPLITS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_NUM_SPLITS) && + verifier.EndTable(); + } + SplitVOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SplitVOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SplitVOptionsBuilder +{ + typedef SplitVOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_num_splits(int32_t num_splits) + { + fbb_.AddElement(SplitVOptions::VT_NUM_SPLITS, num_splits, 0); + } + explicit SplitVOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSplitVOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t num_splits = 0) +{ + SplitVOptionsBuilder builder_(_fbb); + builder_.add_num_splits(num_splits); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSplitVOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct StridedSliceOptionsT : public flatbuffers::NativeTable +{ + typedef StridedSliceOptions TableType; + int32_t begin_mask = 0; + int32_t end_mask = 0; + int32_t ellipsis_mask = 0; + int32_t new_axis_mask = 0; + int32_t shrink_axis_mask = 0; +}; + +struct StridedSliceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef StridedSliceOptionsT NativeTableType; + typedef StridedSliceOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_BEGIN_MASK = 4, + VT_END_MASK = 6, + VT_ELLIPSIS_MASK = 8, + VT_NEW_AXIS_MASK = 10, + VT_SHRINK_AXIS_MASK = 12 + }; + int32_t begin_mask() const { return GetField(VT_BEGIN_MASK, 0); } + int32_t end_mask() const { return GetField(VT_END_MASK, 0); } + int32_t ellipsis_mask() const { return GetField(VT_ELLIPSIS_MASK, 0); } + int32_t new_axis_mask() const { return GetField(VT_NEW_AXIS_MASK, 0); } + int32_t shrink_axis_mask() const { return GetField(VT_SHRINK_AXIS_MASK, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_BEGIN_MASK) && + VerifyField(verifier, VT_END_MASK) && + VerifyField(verifier, VT_ELLIPSIS_MASK) && + VerifyField(verifier, VT_NEW_AXIS_MASK) && + VerifyField(verifier, VT_SHRINK_AXIS_MASK) && verifier.EndTable(); + } + StridedSliceOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(StridedSliceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const StridedSliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct StridedSliceOptionsBuilder +{ + typedef StridedSliceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_begin_mask(int32_t begin_mask) + { + fbb_.AddElement(StridedSliceOptions::VT_BEGIN_MASK, begin_mask, 0); + } + void add_end_mask(int32_t end_mask) + { + fbb_.AddElement(StridedSliceOptions::VT_END_MASK, end_mask, 0); + } + void add_ellipsis_mask(int32_t ellipsis_mask) + { + fbb_.AddElement(StridedSliceOptions::VT_ELLIPSIS_MASK, ellipsis_mask, 0); + } + void add_new_axis_mask(int32_t new_axis_mask) + { + fbb_.AddElement(StridedSliceOptions::VT_NEW_AXIS_MASK, new_axis_mask, 0); + } + void add_shrink_axis_mask(int32_t shrink_axis_mask) + { + fbb_.AddElement(StridedSliceOptions::VT_SHRINK_AXIS_MASK, shrink_axis_mask, 0); + } + explicit StridedSliceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateStridedSliceOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t begin_mask = 0, + int32_t end_mask = 0, int32_t ellipsis_mask = 0, + int32_t new_axis_mask = 0, int32_t shrink_axis_mask = 0) +{ + StridedSliceOptionsBuilder builder_(_fbb); + builder_.add_shrink_axis_mask(shrink_axis_mask); + builder_.add_new_axis_mask(new_axis_mask); + builder_.add_ellipsis_mask(ellipsis_mask); + builder_.add_end_mask(end_mask); + builder_.add_begin_mask(begin_mask); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateStridedSliceOptions(flatbuffers::FlatBufferBuilder &_fbb, const StridedSliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LogSoftmaxOptionsT : public flatbuffers::NativeTable +{ + typedef LogSoftmaxOptions TableType; +}; + +struct LogSoftmaxOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LogSoftmaxOptionsT NativeTableType; + typedef LogSoftmaxOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LogSoftmaxOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LogSoftmaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogSoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LogSoftmaxOptionsBuilder +{ + typedef LogSoftmaxOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LogSoftmaxOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLogSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LogSoftmaxOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLogSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogSoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CastOptionsT : public flatbuffers::NativeTable +{ + typedef CastOptions TableType; + circle::TensorType in_data_type = circle::TensorType_FLOAT32; + circle::TensorType out_data_type = circle::TensorType_FLOAT32; +}; + +struct CastOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CastOptionsT NativeTableType; + typedef CastOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_IN_DATA_TYPE = 4, + VT_OUT_DATA_TYPE = 6 + }; + circle::TensorType in_data_type() const + { + return static_cast(GetField(VT_IN_DATA_TYPE, 0)); + } + circle::TensorType out_data_type() const + { + return static_cast(GetField(VT_OUT_DATA_TYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_IN_DATA_TYPE) && + VerifyField(verifier, VT_OUT_DATA_TYPE) && verifier.EndTable(); + } + CastOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CastOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CastOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CastOptionsBuilder +{ + typedef CastOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_in_data_type(circle::TensorType in_data_type) + { + fbb_.AddElement(CastOptions::VT_IN_DATA_TYPE, static_cast(in_data_type), 0); + } + void add_out_data_type(circle::TensorType out_data_type) + { + fbb_.AddElement(CastOptions::VT_OUT_DATA_TYPE, static_cast(out_data_type), 0); + } + explicit CastOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateCastOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::TensorType in_data_type = circle::TensorType_FLOAT32, + circle::TensorType out_data_type = circle::TensorType_FLOAT32) +{ + CastOptionsBuilder builder_(_fbb); + builder_.add_out_data_type(out_data_type); + builder_.add_in_data_type(in_data_type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateCastOptions(flatbuffers::FlatBufferBuilder &_fbb, const CastOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DequantizeOptionsT : public flatbuffers::NativeTable +{ + typedef DequantizeOptions TableType; +}; + +struct DequantizeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DequantizeOptionsT NativeTableType; + typedef DequantizeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + DequantizeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DequantizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DequantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct DequantizeOptionsBuilder +{ + typedef DequantizeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit DequantizeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateDequantizeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + DequantizeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDequantizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const DequantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MaximumMinimumOptionsT : public flatbuffers::NativeTable +{ + typedef MaximumMinimumOptions TableType; +}; + +struct MaximumMinimumOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MaximumMinimumOptionsT NativeTableType; + typedef MaximumMinimumOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + MaximumMinimumOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MaximumMinimumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MaximumMinimumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MaximumMinimumOptionsBuilder +{ + typedef MaximumMinimumOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit MaximumMinimumOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateMaximumMinimumOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + MaximumMinimumOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateMaximumMinimumOptions(flatbuffers::FlatBufferBuilder &_fbb, const MaximumMinimumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TileOptionsT : public flatbuffers::NativeTable +{ + typedef TileOptions TableType; +}; + +struct TileOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TileOptionsT NativeTableType; + typedef TileOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + TileOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TileOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TileOptionsBuilder +{ + typedef TileOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit TileOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTileOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + TileOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateTileOptions(flatbuffers::FlatBufferBuilder &_fbb, const TileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ArgMaxOptionsT : public flatbuffers::NativeTable +{ + typedef ArgMaxOptions TableType; + circle::TensorType output_type = circle::TensorType_FLOAT32; +}; + +struct ArgMaxOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ArgMaxOptionsT NativeTableType; + typedef ArgMaxOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_OUTPUT_TYPE = 4 + }; + circle::TensorType output_type() const + { + return static_cast(GetField(VT_OUTPUT_TYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_OUTPUT_TYPE) && + verifier.EndTable(); + } + ArgMaxOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ArgMaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArgMaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ArgMaxOptionsBuilder +{ + typedef ArgMaxOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_output_type(circle::TensorType output_type) + { + fbb_.AddElement(ArgMaxOptions::VT_OUTPUT_TYPE, static_cast(output_type), 0); + } + explicit ArgMaxOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateArgMaxOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::TensorType output_type = circle::TensorType_FLOAT32) +{ + ArgMaxOptionsBuilder builder_(_fbb); + builder_.add_output_type(output_type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateArgMaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const ArgMaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ArgMinOptionsT : public flatbuffers::NativeTable +{ + typedef ArgMinOptions TableType; + circle::TensorType output_type = circle::TensorType_FLOAT32; +}; + +struct ArgMinOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ArgMinOptionsT NativeTableType; + typedef ArgMinOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_OUTPUT_TYPE = 4 + }; + circle::TensorType output_type() const + { + return static_cast(GetField(VT_OUTPUT_TYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_OUTPUT_TYPE) && + verifier.EndTable(); + } + ArgMinOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ArgMinOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArgMinOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ArgMinOptionsBuilder +{ + typedef ArgMinOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_output_type(circle::TensorType output_type) + { + fbb_.AddElement(ArgMinOptions::VT_OUTPUT_TYPE, static_cast(output_type), 0); + } + explicit ArgMinOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateArgMinOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::TensorType output_type = circle::TensorType_FLOAT32) +{ + ArgMinOptionsBuilder builder_(_fbb); + builder_.add_output_type(output_type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateArgMinOptions(flatbuffers::FlatBufferBuilder &_fbb, const ArgMinOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct GreaterOptionsT : public flatbuffers::NativeTable +{ + typedef GreaterOptions TableType; +}; + +struct GreaterOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef GreaterOptionsT NativeTableType; + typedef GreaterOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + GreaterOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(GreaterOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const GreaterOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct GreaterOptionsBuilder +{ + typedef GreaterOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit GreaterOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateGreaterOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + GreaterOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateGreaterOptions(flatbuffers::FlatBufferBuilder &_fbb, const GreaterOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct GreaterEqualOptionsT : public flatbuffers::NativeTable +{ + typedef GreaterEqualOptions TableType; +}; + +struct GreaterEqualOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef GreaterEqualOptionsT NativeTableType; + typedef GreaterEqualOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + GreaterEqualOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(GreaterEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const GreaterEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct GreaterEqualOptionsBuilder +{ + typedef GreaterEqualOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit GreaterEqualOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateGreaterEqualOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + GreaterEqualOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateGreaterEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const GreaterEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LessOptionsT : public flatbuffers::NativeTable +{ + typedef LessOptions TableType; +}; + +struct LessOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LessOptionsT NativeTableType; + typedef LessOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LessOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LessOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LessOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LessOptionsBuilder +{ + typedef LessOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LessOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateLessOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LessOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLessOptions(flatbuffers::FlatBufferBuilder &_fbb, const LessOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LessEqualOptionsT : public flatbuffers::NativeTable +{ + typedef LessEqualOptions TableType; +}; + +struct LessEqualOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LessEqualOptionsT NativeTableType; + typedef LessEqualOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LessEqualOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LessEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LessEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LessEqualOptionsBuilder +{ + typedef LessEqualOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LessEqualOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLessEqualOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LessEqualOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLessEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const LessEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct NegOptionsT : public flatbuffers::NativeTable +{ + typedef NegOptions TableType; +}; + +struct NegOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef NegOptionsT NativeTableType; + typedef NegOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + NegOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NegOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const NegOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NegOptionsBuilder +{ + typedef NegOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit NegOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateNegOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + NegOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateNegOptions(flatbuffers::FlatBufferBuilder &_fbb, const NegOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SelectOptionsT : public flatbuffers::NativeTable +{ + typedef SelectOptions TableType; +}; + +struct SelectOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SelectOptionsT NativeTableType; + typedef SelectOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SelectOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SelectOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SelectOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SelectOptionsBuilder +{ + typedef SelectOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SelectOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSelectOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SelectOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSelectOptions(flatbuffers::FlatBufferBuilder &_fbb, const SelectOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SliceOptionsT : public flatbuffers::NativeTable +{ + typedef SliceOptions TableType; +}; + +struct SliceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SliceOptionsT NativeTableType; + typedef SliceOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SliceOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SliceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SliceOptionsBuilder +{ + typedef SliceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SliceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSliceOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SliceOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSliceOptions(flatbuffers::FlatBufferBuilder &_fbb, const SliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TransposeConvOptionsT : public flatbuffers::NativeTable +{ + typedef TransposeConvOptions TableType; + circle::Padding padding = circle::Padding_SAME; + int32_t stride_w = 0; + int32_t stride_h = 0; +}; + +struct TransposeConvOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TransposeConvOptionsT NativeTableType; + typedef TransposeConvOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_PADDING = 4, + VT_STRIDE_W = 6, + VT_STRIDE_H = 8 + }; + circle::Padding padding() const + { + return static_cast(GetField(VT_PADDING, 0)); + } + int32_t stride_w() const { return GetField(VT_STRIDE_W, 0); } + int32_t stride_h() const { return GetField(VT_STRIDE_H, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_PADDING) && + VerifyField(verifier, VT_STRIDE_W) && + VerifyField(verifier, VT_STRIDE_H) && verifier.EndTable(); + } + TransposeConvOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TransposeConvOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TransposeConvOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TransposeConvOptionsBuilder +{ + typedef TransposeConvOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padding(circle::Padding padding) + { + fbb_.AddElement(TransposeConvOptions::VT_PADDING, static_cast(padding), 0); + } + void add_stride_w(int32_t stride_w) + { + fbb_.AddElement(TransposeConvOptions::VT_STRIDE_W, stride_w, 0); + } + void add_stride_h(int32_t stride_h) + { + fbb_.AddElement(TransposeConvOptions::VT_STRIDE_H, stride_h, 0); + } + explicit TransposeConvOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateTransposeConvOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::Padding padding = circle::Padding_SAME, int32_t stride_w = 0, + int32_t stride_h = 0) +{ + TransposeConvOptionsBuilder builder_(_fbb); + builder_.add_stride_h(stride_h); + builder_.add_stride_w(stride_w); + builder_.add_padding(padding); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateTransposeConvOptions(flatbuffers::FlatBufferBuilder &_fbb, const TransposeConvOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ExpandDimsOptionsT : public flatbuffers::NativeTable +{ + typedef ExpandDimsOptions TableType; +}; + +struct ExpandDimsOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ExpandDimsOptionsT NativeTableType; + typedef ExpandDimsOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ExpandDimsOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ExpandDimsOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ExpandDimsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ExpandDimsOptionsBuilder +{ + typedef ExpandDimsOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ExpandDimsOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateExpandDimsOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + ExpandDimsOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateExpandDimsOptions(flatbuffers::FlatBufferBuilder &_fbb, const ExpandDimsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SparseToDenseOptionsT : public flatbuffers::NativeTable +{ + typedef SparseToDenseOptions TableType; + bool validate_indices = false; +}; + +struct SparseToDenseOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SparseToDenseOptionsT NativeTableType; + typedef SparseToDenseOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VALIDATE_INDICES = 4 + }; + bool validate_indices() const { return GetField(VT_VALIDATE_INDICES, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_VALIDATE_INDICES) && + verifier.EndTable(); + } + SparseToDenseOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SparseToDenseOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SparseToDenseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SparseToDenseOptionsBuilder +{ + typedef SparseToDenseOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_validate_indices(bool validate_indices) + { + fbb_.AddElement(SparseToDenseOptions::VT_VALIDATE_INDICES, + static_cast(validate_indices), 0); + } + explicit SparseToDenseOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSparseToDenseOptions(flatbuffers::FlatBufferBuilder &_fbb, bool validate_indices = false) +{ + SparseToDenseOptionsBuilder builder_(_fbb); + builder_.add_validate_indices(validate_indices); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSparseToDenseOptions(flatbuffers::FlatBufferBuilder &_fbb, const SparseToDenseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct EqualOptionsT : public flatbuffers::NativeTable +{ + typedef EqualOptions TableType; +}; + +struct EqualOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef EqualOptionsT NativeTableType; + typedef EqualOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + EqualOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(EqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const EqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct EqualOptionsBuilder +{ + typedef EqualOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit EqualOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateEqualOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + EqualOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const EqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct NotEqualOptionsT : public flatbuffers::NativeTable +{ + typedef NotEqualOptions TableType; +}; + +struct NotEqualOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef NotEqualOptionsT NativeTableType; + typedef NotEqualOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + NotEqualOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NotEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const NotEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NotEqualOptionsBuilder +{ + typedef NotEqualOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit NotEqualOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateNotEqualOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + NotEqualOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateNotEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const NotEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ShapeOptionsT : public flatbuffers::NativeTable +{ + typedef ShapeOptions TableType; + circle::TensorType out_type = circle::TensorType_FLOAT32; +}; + +struct ShapeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ShapeOptionsT NativeTableType; + typedef ShapeOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_OUT_TYPE = 4 + }; + circle::TensorType out_type() const + { + return static_cast(GetField(VT_OUT_TYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_OUT_TYPE) && + verifier.EndTable(); + } + ShapeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ShapeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ShapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ShapeOptionsBuilder +{ + typedef ShapeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_out_type(circle::TensorType out_type) + { + fbb_.AddElement(ShapeOptions::VT_OUT_TYPE, static_cast(out_type), 0); + } + explicit ShapeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateShapeOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::TensorType out_type = circle::TensorType_FLOAT32) +{ + ShapeOptionsBuilder builder_(_fbb); + builder_.add_out_type(out_type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateShapeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ShapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct RankOptionsT : public flatbuffers::NativeTable +{ + typedef RankOptions TableType; +}; + +struct RankOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef RankOptionsT NativeTableType; + typedef RankOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + RankOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(RankOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const RankOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct RankOptionsBuilder +{ + typedef RankOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit RankOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRankOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + RankOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateRankOptions(flatbuffers::FlatBufferBuilder &_fbb, const RankOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct PowOptionsT : public flatbuffers::NativeTable +{ + typedef PowOptions TableType; +}; + +struct PowOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef PowOptionsT NativeTableType; + typedef PowOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + PowOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PowOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const PowOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PowOptionsBuilder +{ + typedef PowOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit PowOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePowOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + PowOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreatePowOptions(flatbuffers::FlatBufferBuilder &_fbb, const PowOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct FakeQuantOptionsT : public flatbuffers::NativeTable +{ + typedef FakeQuantOptions TableType; + float min = 0.0f; + float max = 0.0f; + int32_t num_bits = 0; + bool narrow_range = false; +}; + +struct FakeQuantOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FakeQuantOptionsT NativeTableType; + typedef FakeQuantOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_MIN = 4, + VT_MAX = 6, + VT_NUM_BITS = 8, + VT_NARROW_RANGE = 10 + }; + float min() const { return GetField(VT_MIN, 0.0f); } + float max() const { return GetField(VT_MAX, 0.0f); } + int32_t num_bits() const { return GetField(VT_NUM_BITS, 0); } + bool narrow_range() const { return GetField(VT_NARROW_RANGE, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_MIN) && + VerifyField(verifier, VT_MAX) && VerifyField(verifier, VT_NUM_BITS) && + VerifyField(verifier, VT_NARROW_RANGE) && verifier.EndTable(); + } + FakeQuantOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FakeQuantOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const FakeQuantOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FakeQuantOptionsBuilder +{ + typedef FakeQuantOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_min(float min) { fbb_.AddElement(FakeQuantOptions::VT_MIN, min, 0.0f); } + void add_max(float max) { fbb_.AddElement(FakeQuantOptions::VT_MAX, max, 0.0f); } + void add_num_bits(int32_t num_bits) + { + fbb_.AddElement(FakeQuantOptions::VT_NUM_BITS, num_bits, 0); + } + void add_narrow_range(bool narrow_range) + { + fbb_.AddElement(FakeQuantOptions::VT_NARROW_RANGE, static_cast(narrow_range), + 0); + } + explicit FakeQuantOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateFakeQuantOptions(flatbuffers::FlatBufferBuilder &_fbb, float min = 0.0f, float max = 0.0f, + int32_t num_bits = 0, bool narrow_range = false) +{ + FakeQuantOptionsBuilder builder_(_fbb); + builder_.add_num_bits(num_bits); + builder_.add_max(max); + builder_.add_min(min); + builder_.add_narrow_range(narrow_range); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateFakeQuantOptions(flatbuffers::FlatBufferBuilder &_fbb, const FakeQuantOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct PackOptionsT : public flatbuffers::NativeTable +{ + typedef PackOptions TableType; + int32_t values_count = 0; + int32_t axis = 0; +}; + +struct PackOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef PackOptionsT NativeTableType; + typedef PackOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VALUES_COUNT = 4, + VT_AXIS = 6 + }; + int32_t values_count() const { return GetField(VT_VALUES_COUNT, 0); } + int32_t axis() const { return GetField(VT_AXIS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_VALUES_COUNT) && + VerifyField(verifier, VT_AXIS) && verifier.EndTable(); + } + PackOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(PackOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const PackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct PackOptionsBuilder +{ + typedef PackOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_values_count(int32_t values_count) + { + fbb_.AddElement(PackOptions::VT_VALUES_COUNT, values_count, 0); + } + void add_axis(int32_t axis) { fbb_.AddElement(PackOptions::VT_AXIS, axis, 0); } + explicit PackOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreatePackOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t values_count = 0, int32_t axis = 0) +{ + PackOptionsBuilder builder_(_fbb); + builder_.add_axis(axis); + builder_.add_values_count(values_count); + return builder_.Finish(); +} + +flatbuffers::Offset +CreatePackOptions(flatbuffers::FlatBufferBuilder &_fbb, const PackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LogicalOrOptionsT : public flatbuffers::NativeTable +{ + typedef LogicalOrOptions TableType; +}; + +struct LogicalOrOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LogicalOrOptionsT NativeTableType; + typedef LogicalOrOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LogicalOrOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LogicalOrOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalOrOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LogicalOrOptionsBuilder +{ + typedef LogicalOrOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LogicalOrOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLogicalOrOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LogicalOrOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLogicalOrOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalOrOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct OneHotOptionsT : public flatbuffers::NativeTable +{ + typedef OneHotOptions TableType; + int32_t axis = 0; +}; + +struct OneHotOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef OneHotOptionsT NativeTableType; + typedef OneHotOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_AXIS = 4 + }; + int32_t axis() const { return GetField(VT_AXIS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_AXIS) && + verifier.EndTable(); + } + OneHotOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(OneHotOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const OneHotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct OneHotOptionsBuilder +{ + typedef OneHotOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_axis(int32_t axis) { fbb_.AddElement(OneHotOptions::VT_AXIS, axis, 0); } + explicit OneHotOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateOneHotOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t axis = 0) +{ + OneHotOptionsBuilder builder_(_fbb); + builder_.add_axis(axis); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateOneHotOptions(flatbuffers::FlatBufferBuilder &_fbb, const OneHotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct AbsOptionsT : public flatbuffers::NativeTable +{ + typedef AbsOptions TableType; +}; + +struct AbsOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef AbsOptionsT NativeTableType; + typedef AbsOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + AbsOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AbsOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AbsOptionsBuilder +{ + typedef AbsOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit AbsOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAbsOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + AbsOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateAbsOptions(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct HardSwishOptionsT : public flatbuffers::NativeTable +{ + typedef HardSwishOptions TableType; +}; + +struct HardSwishOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef HardSwishOptionsT NativeTableType; + typedef HardSwishOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + HardSwishOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(HardSwishOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const HardSwishOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct HardSwishOptionsBuilder +{ + typedef HardSwishOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit HardSwishOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateHardSwishOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + HardSwishOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateHardSwishOptions(flatbuffers::FlatBufferBuilder &_fbb, const HardSwishOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LogicalAndOptionsT : public flatbuffers::NativeTable +{ + typedef LogicalAndOptions TableType; +}; + +struct LogicalAndOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LogicalAndOptionsT NativeTableType; + typedef LogicalAndOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LogicalAndOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LogicalAndOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalAndOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LogicalAndOptionsBuilder +{ + typedef LogicalAndOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LogicalAndOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLogicalAndOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LogicalAndOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLogicalAndOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalAndOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LogicalNotOptionsT : public flatbuffers::NativeTable +{ + typedef LogicalNotOptions TableType; +}; + +struct LogicalNotOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LogicalNotOptionsT NativeTableType; + typedef LogicalNotOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + LogicalNotOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LogicalNotOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalNotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LogicalNotOptionsBuilder +{ + typedef LogicalNotOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit LogicalNotOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLogicalNotOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + LogicalNotOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLogicalNotOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalNotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct UnpackOptionsT : public flatbuffers::NativeTable +{ + typedef UnpackOptions TableType; + int32_t num = 0; + int32_t axis = 0; +}; + +struct UnpackOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef UnpackOptionsT NativeTableType; + typedef UnpackOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NUM = 4, + VT_AXIS = 6 + }; + int32_t num() const { return GetField(VT_NUM, 0); } + int32_t axis() const { return GetField(VT_AXIS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_NUM) && + VerifyField(verifier, VT_AXIS) && verifier.EndTable(); + } + UnpackOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(UnpackOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnpackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct UnpackOptionsBuilder +{ + typedef UnpackOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_num(int32_t num) { fbb_.AddElement(UnpackOptions::VT_NUM, num, 0); } + void add_axis(int32_t axis) { fbb_.AddElement(UnpackOptions::VT_AXIS, axis, 0); } + explicit UnpackOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateUnpackOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t num = 0, int32_t axis = 0) +{ + UnpackOptionsBuilder builder_(_fbb); + builder_.add_axis(axis); + builder_.add_num(num); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateUnpackOptions(flatbuffers::FlatBufferBuilder &_fbb, const UnpackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct FloorDivOptionsT : public flatbuffers::NativeTable +{ + typedef FloorDivOptions TableType; +}; + +struct FloorDivOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FloorDivOptionsT NativeTableType; + typedef FloorDivOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + FloorDivOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FloorDivOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const FloorDivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FloorDivOptionsBuilder +{ + typedef FloorDivOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit FloorDivOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateFloorDivOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + FloorDivOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateFloorDivOptions(flatbuffers::FlatBufferBuilder &_fbb, const FloorDivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SquareOptionsT : public flatbuffers::NativeTable +{ + typedef SquareOptions TableType; +}; + +struct SquareOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SquareOptionsT NativeTableType; + typedef SquareOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SquareOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SquareOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SquareOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SquareOptionsBuilder +{ + typedef SquareOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SquareOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSquareOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SquareOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSquareOptions(flatbuffers::FlatBufferBuilder &_fbb, const SquareOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ZerosLikeOptionsT : public flatbuffers::NativeTable +{ + typedef ZerosLikeOptions TableType; +}; + +struct ZerosLikeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ZerosLikeOptionsT NativeTableType; + typedef ZerosLikeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ZerosLikeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ZerosLikeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ZerosLikeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ZerosLikeOptionsBuilder +{ + typedef ZerosLikeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ZerosLikeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateZerosLikeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + ZerosLikeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateZerosLikeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ZerosLikeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct FillOptionsT : public flatbuffers::NativeTable +{ + typedef FillOptions TableType; +}; + +struct FillOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FillOptionsT NativeTableType; + typedef FillOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + FillOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FillOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const FillOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FillOptionsBuilder +{ + typedef FillOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit FillOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateFillOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + FillOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateFillOptions(flatbuffers::FlatBufferBuilder &_fbb, const FillOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct FloorModOptionsT : public flatbuffers::NativeTable +{ + typedef FloorModOptions TableType; +}; + +struct FloorModOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef FloorModOptionsT NativeTableType; + typedef FloorModOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + FloorModOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(FloorModOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const FloorModOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct FloorModOptionsBuilder +{ + typedef FloorModOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit FloorModOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateFloorModOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + FloorModOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateFloorModOptions(flatbuffers::FlatBufferBuilder &_fbb, const FloorModOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct RangeOptionsT : public flatbuffers::NativeTable +{ + typedef RangeOptions TableType; +}; + +struct RangeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef RangeOptionsT NativeTableType; + typedef RangeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + RangeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(RangeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const RangeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct RangeOptionsBuilder +{ + typedef RangeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit RangeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRangeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + RangeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateRangeOptions(flatbuffers::FlatBufferBuilder &_fbb, const RangeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct LeakyReluOptionsT : public flatbuffers::NativeTable +{ + typedef LeakyReluOptions TableType; + float alpha = 0.0f; +}; + +struct LeakyReluOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef LeakyReluOptionsT NativeTableType; + typedef LeakyReluOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_ALPHA = 4 + }; + float alpha() const { return GetField(VT_ALPHA, 0.0f); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_ALPHA) && + verifier.EndTable(); + } + LeakyReluOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(LeakyReluOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct LeakyReluOptionsBuilder +{ + typedef LeakyReluOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_alpha(float alpha) { fbb_.AddElement(LeakyReluOptions::VT_ALPHA, alpha, 0.0f); } + explicit LeakyReluOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateLeakyReluOptions(flatbuffers::FlatBufferBuilder &_fbb, float alpha = 0.0f) +{ + LeakyReluOptionsBuilder builder_(_fbb); + builder_.add_alpha(alpha); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateLeakyReluOptions(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SquaredDifferenceOptionsT : public flatbuffers::NativeTable +{ + typedef SquaredDifferenceOptions TableType; +}; + +struct SquaredDifferenceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SquaredDifferenceOptionsT NativeTableType; + typedef SquaredDifferenceOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SquaredDifferenceOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SquaredDifferenceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SquaredDifferenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SquaredDifferenceOptionsBuilder +{ + typedef SquaredDifferenceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SquaredDifferenceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SquaredDifferenceOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &_fbb, + const SquaredDifferenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MirrorPadOptionsT : public flatbuffers::NativeTable +{ + typedef MirrorPadOptions TableType; + circle::MirrorPadMode mode = circle::MirrorPadMode_REFLECT; +}; + +struct MirrorPadOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MirrorPadOptionsT NativeTableType; + typedef MirrorPadOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_MODE = 4 + }; + circle::MirrorPadMode mode() const + { + return static_cast(GetField(VT_MODE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_MODE) && + verifier.EndTable(); + } + MirrorPadOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MirrorPadOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MirrorPadOptionsBuilder +{ + typedef MirrorPadOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_mode(circle::MirrorPadMode mode) + { + fbb_.AddElement(MirrorPadOptions::VT_MODE, static_cast(mode), 0); + } + explicit MirrorPadOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::MirrorPadMode mode = circle::MirrorPadMode_REFLECT) +{ + MirrorPadOptionsBuilder builder_(_fbb); + builder_.add_mode(mode); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct UniqueOptionsT : public flatbuffers::NativeTable +{ + typedef UniqueOptions TableType; + circle::TensorType idx_out_type = circle::TensorType_INT32; +}; + +struct UniqueOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef UniqueOptionsT NativeTableType; + typedef UniqueOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_IDX_OUT_TYPE = 4 + }; + circle::TensorType idx_out_type() const + { + return static_cast(GetField(VT_IDX_OUT_TYPE, 2)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_IDX_OUT_TYPE) && + verifier.EndTable(); + } + UniqueOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(UniqueOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct UniqueOptionsBuilder +{ + typedef UniqueOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_idx_out_type(circle::TensorType idx_out_type) + { + fbb_.AddElement(UniqueOptions::VT_IDX_OUT_TYPE, static_cast(idx_out_type), 2); + } + explicit UniqueOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateUniqueOptions(flatbuffers::FlatBufferBuilder &_fbb, + circle::TensorType idx_out_type = circle::TensorType_INT32) +{ + UniqueOptionsBuilder builder_(_fbb); + builder_.add_idx_out_type(idx_out_type); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateUniqueOptions(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ReverseV2OptionsT : public flatbuffers::NativeTable +{ + typedef ReverseV2Options TableType; +}; + +struct ReverseV2Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ReverseV2OptionsT NativeTableType; + typedef ReverseV2OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ReverseV2OptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ReverseV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReverseV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ReverseV2OptionsBuilder +{ + typedef ReverseV2Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ReverseV2OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateReverseV2Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + ReverseV2OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateReverseV2Options(flatbuffers::FlatBufferBuilder &_fbb, const ReverseV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct AddNOptionsT : public flatbuffers::NativeTable +{ + typedef AddNOptions TableType; +}; + +struct AddNOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef AddNOptionsT NativeTableType; + typedef AddNOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + AddNOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AddNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const AddNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AddNOptionsBuilder +{ + typedef AddNOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit AddNOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAddNOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + AddNOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateAddNOptions(flatbuffers::FlatBufferBuilder &_fbb, const AddNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct GatherNdOptionsT : public flatbuffers::NativeTable +{ + typedef GatherNdOptions TableType; +}; + +struct GatherNdOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef GatherNdOptionsT NativeTableType; + typedef GatherNdOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + GatherNdOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(GatherNdOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const GatherNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct GatherNdOptionsBuilder +{ + typedef GatherNdOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit GatherNdOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateGatherNdOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + GatherNdOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateGatherNdOptions(flatbuffers::FlatBufferBuilder &_fbb, const GatherNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct WhereOptionsT : public flatbuffers::NativeTable +{ + typedef WhereOptions TableType; +}; + +struct WhereOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef WhereOptionsT NativeTableType; + typedef WhereOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + WhereOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(WhereOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const WhereOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct WhereOptionsBuilder +{ + typedef WhereOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit WhereOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateWhereOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + WhereOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateWhereOptions(flatbuffers::FlatBufferBuilder &_fbb, const WhereOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ReverseSequenceOptionsT : public flatbuffers::NativeTable +{ + typedef ReverseSequenceOptions TableType; + int32_t seq_dim = 0; + int32_t batch_dim = 0; +}; + +struct ReverseSequenceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ReverseSequenceOptionsT NativeTableType; + typedef ReverseSequenceOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_SEQ_DIM = 4, + VT_BATCH_DIM = 6 + }; + int32_t seq_dim() const { return GetField(VT_SEQ_DIM, 0); } + int32_t batch_dim() const { return GetField(VT_BATCH_DIM, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_SEQ_DIM) && + VerifyField(verifier, VT_BATCH_DIM) && verifier.EndTable(); + } + ReverseSequenceOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ReverseSequenceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReverseSequenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ReverseSequenceOptionsBuilder +{ + typedef ReverseSequenceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_seq_dim(int32_t seq_dim) + { + fbb_.AddElement(ReverseSequenceOptions::VT_SEQ_DIM, seq_dim, 0); + } + void add_batch_dim(int32_t batch_dim) + { + fbb_.AddElement(ReverseSequenceOptions::VT_BATCH_DIM, batch_dim, 0); + } + explicit ReverseSequenceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateReverseSequenceOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t seq_dim = 0, + int32_t batch_dim = 0) +{ + ReverseSequenceOptionsBuilder builder_(_fbb); + builder_.add_batch_dim(batch_dim); + builder_.add_seq_dim(seq_dim); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateReverseSequenceOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ReverseSequenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MatrixDiagOptionsT : public flatbuffers::NativeTable +{ + typedef MatrixDiagOptions TableType; +}; + +struct MatrixDiagOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MatrixDiagOptionsT NativeTableType; + typedef MatrixDiagOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + MatrixDiagOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MatrixDiagOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MatrixDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MatrixDiagOptionsBuilder +{ + typedef MatrixDiagOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit MatrixDiagOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateMatrixDiagOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + MatrixDiagOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateMatrixDiagOptions(flatbuffers::FlatBufferBuilder &_fbb, const MatrixDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct QuantizeOptionsT : public flatbuffers::NativeTable +{ + typedef QuantizeOptions TableType; +}; + +struct QuantizeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef QuantizeOptionsT NativeTableType; + typedef QuantizeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + QuantizeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(QuantizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const QuantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct QuantizeOptionsBuilder +{ + typedef QuantizeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit QuantizeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateQuantizeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + QuantizeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateQuantizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const QuantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MatrixSetDiagOptionsT : public flatbuffers::NativeTable +{ + typedef MatrixSetDiagOptions TableType; +}; + +struct MatrixSetDiagOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MatrixSetDiagOptionsT NativeTableType; + typedef MatrixSetDiagOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + MatrixSetDiagOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MatrixSetDiagOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MatrixSetDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MatrixSetDiagOptionsBuilder +{ + typedef MatrixSetDiagOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit MatrixSetDiagOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateMatrixSetDiagOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + MatrixSetDiagOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateMatrixSetDiagOptions(flatbuffers::FlatBufferBuilder &_fbb, const MatrixSetDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct IfOptionsT : public flatbuffers::NativeTable +{ + typedef IfOptions TableType; + int32_t then_subgraph_index = 0; + int32_t else_subgraph_index = 0; +}; + +struct IfOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef IfOptionsT NativeTableType; + typedef IfOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_THEN_SUBGRAPH_INDEX = 4, + VT_ELSE_SUBGRAPH_INDEX = 6 + }; + int32_t then_subgraph_index() const { return GetField(VT_THEN_SUBGRAPH_INDEX, 0); } + int32_t else_subgraph_index() const { return GetField(VT_ELSE_SUBGRAPH_INDEX, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_THEN_SUBGRAPH_INDEX) && + VerifyField(verifier, VT_ELSE_SUBGRAPH_INDEX) && verifier.EndTable(); + } + IfOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(IfOptionsT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const IfOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct IfOptionsBuilder +{ + typedef IfOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_then_subgraph_index(int32_t then_subgraph_index) + { + fbb_.AddElement(IfOptions::VT_THEN_SUBGRAPH_INDEX, then_subgraph_index, 0); + } + void add_else_subgraph_index(int32_t else_subgraph_index) + { + fbb_.AddElement(IfOptions::VT_ELSE_SUBGRAPH_INDEX, else_subgraph_index, 0); + } + explicit IfOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateIfOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t then_subgraph_index = 0, + int32_t else_subgraph_index = 0) +{ + IfOptionsBuilder builder_(_fbb); + builder_.add_else_subgraph_index(else_subgraph_index); + builder_.add_then_subgraph_index(then_subgraph_index); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateIfOptions(flatbuffers::FlatBufferBuilder &_fbb, const IfOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CallOnceOptionsT : public flatbuffers::NativeTable +{ + typedef CallOnceOptions TableType; + int32_t init_subgraph_index = 0; +}; + +struct CallOnceOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CallOnceOptionsT NativeTableType; + typedef CallOnceOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_INIT_SUBGRAPH_INDEX = 4 + }; + int32_t init_subgraph_index() const { return GetField(VT_INIT_SUBGRAPH_INDEX, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_INIT_SUBGRAPH_INDEX) && + verifier.EndTable(); + } + CallOnceOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CallOnceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CallOnceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CallOnceOptionsBuilder +{ + typedef CallOnceOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_init_subgraph_index(int32_t init_subgraph_index) + { + fbb_.AddElement(CallOnceOptions::VT_INIT_SUBGRAPH_INDEX, init_subgraph_index, 0); + } + explicit CallOnceOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateCallOnceOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t init_subgraph_index = 0) +{ + CallOnceOptionsBuilder builder_(_fbb); + builder_.add_init_subgraph_index(init_subgraph_index); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateCallOnceOptions(flatbuffers::FlatBufferBuilder &_fbb, const CallOnceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct WhileOptionsT : public flatbuffers::NativeTable +{ + typedef WhileOptions TableType; + int32_t cond_subgraph_index = 0; + int32_t body_subgraph_index = 0; +}; + +struct WhileOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef WhileOptionsT NativeTableType; + typedef WhileOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_COND_SUBGRAPH_INDEX = 4, + VT_BODY_SUBGRAPH_INDEX = 6 + }; + int32_t cond_subgraph_index() const { return GetField(VT_COND_SUBGRAPH_INDEX, 0); } + int32_t body_subgraph_index() const { return GetField(VT_BODY_SUBGRAPH_INDEX, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_COND_SUBGRAPH_INDEX) && + VerifyField(verifier, VT_BODY_SUBGRAPH_INDEX) && verifier.EndTable(); + } + WhileOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(WhileOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const WhileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct WhileOptionsBuilder +{ + typedef WhileOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_cond_subgraph_index(int32_t cond_subgraph_index) + { + fbb_.AddElement(WhileOptions::VT_COND_SUBGRAPH_INDEX, cond_subgraph_index, 0); + } + void add_body_subgraph_index(int32_t body_subgraph_index) + { + fbb_.AddElement(WhileOptions::VT_BODY_SUBGRAPH_INDEX, body_subgraph_index, 0); + } + explicit WhileOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateWhileOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t cond_subgraph_index = 0, + int32_t body_subgraph_index = 0) +{ + WhileOptionsBuilder builder_(_fbb); + builder_.add_body_subgraph_index(body_subgraph_index); + builder_.add_cond_subgraph_index(cond_subgraph_index); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateWhileOptions(flatbuffers::FlatBufferBuilder &_fbb, const WhileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct NonMaxSuppressionV4OptionsT : public flatbuffers::NativeTable +{ + typedef NonMaxSuppressionV4Options TableType; +}; + +struct NonMaxSuppressionV4Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef NonMaxSuppressionV4OptionsT NativeTableType; + typedef NonMaxSuppressionV4OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + NonMaxSuppressionV4OptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NonMaxSuppressionV4OptionsBuilder +{ + typedef NonMaxSuppressionV4Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit NonMaxSuppressionV4OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateNonMaxSuppressionV4Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + NonMaxSuppressionV4OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateNonMaxSuppressionV4Options(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct NonMaxSuppressionV5OptionsT : public flatbuffers::NativeTable +{ + typedef NonMaxSuppressionV5Options TableType; +}; + +struct NonMaxSuppressionV5Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef NonMaxSuppressionV5OptionsT NativeTableType; + typedef NonMaxSuppressionV5OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + NonMaxSuppressionV5OptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NonMaxSuppressionV5OptionsBuilder +{ + typedef NonMaxSuppressionV5Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit NonMaxSuppressionV5OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateNonMaxSuppressionV5Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + NonMaxSuppressionV5OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateNonMaxSuppressionV5Options(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ScatterNdOptionsT : public flatbuffers::NativeTable +{ + typedef ScatterNdOptions TableType; +}; + +struct ScatterNdOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ScatterNdOptionsT NativeTableType; + typedef ScatterNdOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ScatterNdOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ScatterNdOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ScatterNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ScatterNdOptionsBuilder +{ + typedef ScatterNdOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ScatterNdOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateScatterNdOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + ScatterNdOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateScatterNdOptions(flatbuffers::FlatBufferBuilder &_fbb, const ScatterNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SelectV2OptionsT : public flatbuffers::NativeTable +{ + typedef SelectV2Options TableType; +}; + +struct SelectV2Options FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SelectV2OptionsT NativeTableType; + typedef SelectV2OptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SelectV2OptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SelectV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SelectV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SelectV2OptionsBuilder +{ + typedef SelectV2Options Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SelectV2OptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSelectV2Options(flatbuffers::FlatBufferBuilder &_fbb) +{ + SelectV2OptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSelectV2Options(flatbuffers::FlatBufferBuilder &_fbb, const SelectV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct DensifyOptionsT : public flatbuffers::NativeTable +{ + typedef DensifyOptions TableType; +}; + +struct DensifyOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef DensifyOptionsT NativeTableType; + typedef DensifyOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + DensifyOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(DensifyOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const DensifyOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct DensifyOptionsBuilder +{ + typedef DensifyOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit DensifyOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateDensifyOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + DensifyOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateDensifyOptions(flatbuffers::FlatBufferBuilder &_fbb, const DensifyOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SegmentSumOptionsT : public flatbuffers::NativeTable +{ + typedef SegmentSumOptions TableType; +}; + +struct SegmentSumOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SegmentSumOptionsT NativeTableType; + typedef SegmentSumOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + SegmentSumOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SegmentSumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SegmentSumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SegmentSumOptionsBuilder +{ + typedef SegmentSumOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit SegmentSumOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateSegmentSumOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + SegmentSumOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateSegmentSumOptions(flatbuffers::FlatBufferBuilder &_fbb, const SegmentSumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BatchMatMulOptionsT : public flatbuffers::NativeTable +{ + typedef BatchMatMulOptions TableType; + bool adjoint_lhs = false; + bool adjoint_rhs = false; + bool asymmetric_quantize_inputs = false; +}; + +struct BatchMatMulOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BatchMatMulOptionsT NativeTableType; + typedef BatchMatMulOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_ADJOINT_LHS = 4, + VT_ADJOINT_RHS = 6, + VT_ASYMMETRIC_QUANTIZE_INPUTS = 8 + }; + bool adjoint_lhs() const { return GetField(VT_ADJOINT_LHS, 0) != 0; } + bool adjoint_rhs() const { return GetField(VT_ADJOINT_RHS, 0) != 0; } + bool asymmetric_quantize_inputs() const + { + return GetField(VT_ASYMMETRIC_QUANTIZE_INPUTS, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_ADJOINT_LHS) && + VerifyField(verifier, VT_ADJOINT_RHS) && + VerifyField(verifier, VT_ASYMMETRIC_QUANTIZE_INPUTS) && verifier.EndTable(); + } + BatchMatMulOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BatchMatMulOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BatchMatMulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BatchMatMulOptionsBuilder +{ + typedef BatchMatMulOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_adjoint_lhs(bool adjoint_lhs) + { + fbb_.AddElement(BatchMatMulOptions::VT_ADJOINT_LHS, static_cast(adjoint_lhs), + 0); + } + void add_adjoint_rhs(bool adjoint_rhs) + { + fbb_.AddElement(BatchMatMulOptions::VT_ADJOINT_RHS, static_cast(adjoint_rhs), + 0); + } + void add_asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + fbb_.AddElement(BatchMatMulOptions::VT_ASYMMETRIC_QUANTIZE_INPUTS, + static_cast(asymmetric_quantize_inputs), 0); + } + explicit BatchMatMulOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateBatchMatMulOptions(flatbuffers::FlatBufferBuilder &_fbb, bool adjoint_lhs = false, + bool adjoint_rhs = false, bool asymmetric_quantize_inputs = false) +{ + BatchMatMulOptionsBuilder builder_(_fbb); + builder_.add_asymmetric_quantize_inputs(asymmetric_quantize_inputs); + builder_.add_adjoint_rhs(adjoint_rhs); + builder_.add_adjoint_lhs(adjoint_lhs); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBatchMatMulOptions(flatbuffers::FlatBufferBuilder &_fbb, const BatchMatMulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CumsumOptionsT : public flatbuffers::NativeTable +{ + typedef CumsumOptions TableType; + bool exclusive = false; + bool reverse = false; +}; + +struct CumsumOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef CumsumOptionsT NativeTableType; + typedef CumsumOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_EXCLUSIVE = 4, + VT_REVERSE = 6 + }; + bool exclusive() const { return GetField(VT_EXCLUSIVE, 0) != 0; } + bool reverse() const { return GetField(VT_REVERSE, 0) != 0; } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_EXCLUSIVE) && + VerifyField(verifier, VT_REVERSE) && verifier.EndTable(); + } + CumsumOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CumsumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const CumsumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CumsumOptionsBuilder +{ + typedef CumsumOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_exclusive(bool exclusive) + { + fbb_.AddElement(CumsumOptions::VT_EXCLUSIVE, static_cast(exclusive), 0); + } + void add_reverse(bool reverse) + { + fbb_.AddElement(CumsumOptions::VT_REVERSE, static_cast(reverse), 0); + } + explicit CumsumOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateCumsumOptions(flatbuffers::FlatBufferBuilder &_fbb, + bool exclusive = false, + bool reverse = false) +{ + CumsumOptionsBuilder builder_(_fbb); + builder_.add_reverse(reverse); + builder_.add_exclusive(exclusive); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateCumsumOptions(flatbuffers::FlatBufferBuilder &_fbb, const CumsumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BroadcastToOptionsT : public flatbuffers::NativeTable +{ + typedef BroadcastToOptions TableType; +}; + +struct BroadcastToOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BroadcastToOptionsT NativeTableType; + typedef BroadcastToOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + BroadcastToOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BroadcastToOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BroadcastToOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BroadcastToOptionsBuilder +{ + typedef BroadcastToOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit BroadcastToOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateBroadcastToOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + BroadcastToOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBroadcastToOptions(flatbuffers::FlatBufferBuilder &_fbb, const BroadcastToOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct Rfft2dOptionsT : public flatbuffers::NativeTable +{ + typedef Rfft2dOptions TableType; +}; + +struct Rfft2dOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef Rfft2dOptionsT NativeTableType; + typedef Rfft2dOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + Rfft2dOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Rfft2dOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const Rfft2dOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct Rfft2dOptionsBuilder +{ + typedef Rfft2dOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit Rfft2dOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRfft2dOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + Rfft2dOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateRfft2dOptions(flatbuffers::FlatBufferBuilder &_fbb, const Rfft2dOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct HashtableOptionsT : public flatbuffers::NativeTable +{ + typedef HashtableOptions TableType; + int32_t table_id = 0; + circle::TensorType key_dtype = circle::TensorType_FLOAT32; + circle::TensorType value_dtype = circle::TensorType_FLOAT32; +}; + +struct HashtableOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef HashtableOptionsT NativeTableType; + typedef HashtableOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TABLE_ID = 4, + VT_KEY_DTYPE = 6, + VT_VALUE_DTYPE = 8 + }; + int32_t table_id() const { return GetField(VT_TABLE_ID, 0); } + circle::TensorType key_dtype() const + { + return static_cast(GetField(VT_KEY_DTYPE, 0)); + } + circle::TensorType value_dtype() const + { + return static_cast(GetField(VT_VALUE_DTYPE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_TABLE_ID) && + VerifyField(verifier, VT_KEY_DTYPE) && + VerifyField(verifier, VT_VALUE_DTYPE) && verifier.EndTable(); + } + HashtableOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(HashtableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct HashtableOptionsBuilder +{ + typedef HashtableOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_table_id(int32_t table_id) + { + fbb_.AddElement(HashtableOptions::VT_TABLE_ID, table_id, 0); + } + void add_key_dtype(circle::TensorType key_dtype) + { + fbb_.AddElement(HashtableOptions::VT_KEY_DTYPE, static_cast(key_dtype), 0); + } + void add_value_dtype(circle::TensorType value_dtype) + { + fbb_.AddElement(HashtableOptions::VT_VALUE_DTYPE, static_cast(value_dtype), 0); + } + explicit HashtableOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateHashtableOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t table_id = 0, + circle::TensorType key_dtype = circle::TensorType_FLOAT32, + circle::TensorType value_dtype = circle::TensorType_FLOAT32) +{ + HashtableOptionsBuilder builder_(_fbb); + builder_.add_table_id(table_id); + builder_.add_value_dtype(value_dtype); + builder_.add_key_dtype(key_dtype); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateHashtableOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct HashtableFindOptionsT : public flatbuffers::NativeTable +{ + typedef HashtableFindOptions TableType; +}; + +struct HashtableFindOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef HashtableFindOptionsT NativeTableType; + typedef HashtableFindOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + HashtableFindOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(HashtableFindOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableFindOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct HashtableFindOptionsBuilder +{ + typedef HashtableFindOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit HashtableFindOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateHashtableFindOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + HashtableFindOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateHashtableFindOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableFindOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct HashtableImportOptionsT : public flatbuffers::NativeTable +{ + typedef HashtableImportOptions TableType; +}; + +struct HashtableImportOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef HashtableImportOptionsT NativeTableType; + typedef HashtableImportOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + HashtableImportOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(HashtableImportOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableImportOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct HashtableImportOptionsBuilder +{ + typedef HashtableImportOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit HashtableImportOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateHashtableImportOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + HashtableImportOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateHashtableImportOptions(flatbuffers::FlatBufferBuilder &_fbb, + const HashtableImportOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct HashtableSizeOptionsT : public flatbuffers::NativeTable +{ + typedef HashtableSizeOptions TableType; +}; + +struct HashtableSizeOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef HashtableSizeOptionsT NativeTableType; + typedef HashtableSizeOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + HashtableSizeOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(HashtableSizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableSizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct HashtableSizeOptionsBuilder +{ + typedef HashtableSizeOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit HashtableSizeOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateHashtableSizeOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + HashtableSizeOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateHashtableSizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableSizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct VarHandleOptionsT : public flatbuffers::NativeTable +{ + typedef VarHandleOptions TableType; + std::string container{}; + std::string shared_name{}; +}; + +struct VarHandleOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef VarHandleOptionsT NativeTableType; + typedef VarHandleOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_CONTAINER = 4, + VT_SHARED_NAME = 6 + }; + const flatbuffers::String *container() const + { + return GetPointer(VT_CONTAINER); + } + const flatbuffers::String *shared_name() const + { + return GetPointer(VT_SHARED_NAME); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_CONTAINER) && + verifier.VerifyString(container()) && VerifyOffset(verifier, VT_SHARED_NAME) && + verifier.VerifyString(shared_name()) && verifier.EndTable(); + } + VarHandleOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(VarHandleOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const VarHandleOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct VarHandleOptionsBuilder +{ + typedef VarHandleOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_container(flatbuffers::Offset container) + { + fbb_.AddOffset(VarHandleOptions::VT_CONTAINER, container); + } + void add_shared_name(flatbuffers::Offset shared_name) + { + fbb_.AddOffset(VarHandleOptions::VT_SHARED_NAME, shared_name); + } + explicit VarHandleOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateVarHandleOptions(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset container = 0, + flatbuffers::Offset shared_name = 0) +{ + VarHandleOptionsBuilder builder_(_fbb); + builder_.add_shared_name(shared_name); + builder_.add_container(container); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateVarHandleOptionsDirect(flatbuffers::FlatBufferBuilder &_fbb, const char *container = nullptr, + const char *shared_name = nullptr) +{ + auto container__ = container ? _fbb.CreateString(container) : 0; + auto shared_name__ = shared_name ? _fbb.CreateString(shared_name) : 0; + return circle::CreateVarHandleOptions(_fbb, container__, shared_name__); +} + +flatbuffers::Offset +CreateVarHandleOptions(flatbuffers::FlatBufferBuilder &_fbb, const VarHandleOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ReadVariableOptionsT : public flatbuffers::NativeTable +{ + typedef ReadVariableOptions TableType; +}; + +struct ReadVariableOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ReadVariableOptionsT NativeTableType; + typedef ReadVariableOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + ReadVariableOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ReadVariableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReadVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ReadVariableOptionsBuilder +{ + typedef ReadVariableOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit ReadVariableOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateReadVariableOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + ReadVariableOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateReadVariableOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReadVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct AssignVariableOptionsT : public flatbuffers::NativeTable +{ + typedef AssignVariableOptions TableType; +}; + +struct AssignVariableOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef AssignVariableOptionsT NativeTableType; + typedef AssignVariableOptionsBuilder Builder; + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && verifier.EndTable(); + } + AssignVariableOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AssignVariableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const AssignVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AssignVariableOptionsBuilder +{ + typedef AssignVariableOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + explicit AssignVariableOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateAssignVariableOptions(flatbuffers::FlatBufferBuilder &_fbb) +{ + AssignVariableOptionsBuilder builder_(_fbb); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateAssignVariableOptions(flatbuffers::FlatBufferBuilder &_fbb, const AssignVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct RandomOptionsT : public flatbuffers::NativeTable +{ + typedef RandomOptions TableType; + int32_t seed = 0; + int32_t seed2 = 0; +}; + +struct RandomOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef RandomOptionsT NativeTableType; + typedef RandomOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_SEED = 4, + VT_SEED2 = 6 + }; + int32_t seed() const { return GetField(VT_SEED, 0); } + int32_t seed2() const { return GetField(VT_SEED2, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_SEED) && + VerifyField(verifier, VT_SEED2) && verifier.EndTable(); + } + RandomOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(RandomOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct RandomOptionsBuilder +{ + typedef RandomOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_seed(int32_t seed) { fbb_.AddElement(RandomOptions::VT_SEED, seed, 0); } + void add_seed2(int32_t seed2) { fbb_.AddElement(RandomOptions::VT_SEED2, seed2, 0); } + explicit RandomOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRandomOptions(flatbuffers::FlatBufferBuilder &_fbb, + int32_t seed = 0, int32_t seed2 = 0) +{ + RandomOptionsBuilder builder_(_fbb); + builder_.add_seed2(seed2); + builder_.add_seed(seed); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateRandomOptions(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BCQGatherOptionsT : public flatbuffers::NativeTable +{ + typedef BCQGatherOptions TableType; + int32_t input_hidden_size = 0; + int32_t axis = 0; +}; + +struct BCQGatherOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BCQGatherOptionsT NativeTableType; + typedef BCQGatherOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_INPUT_HIDDEN_SIZE = 4, + VT_AXIS = 6 + }; + int32_t input_hidden_size() const { return GetField(VT_INPUT_HIDDEN_SIZE, 0); } + int32_t axis() const { return GetField(VT_AXIS, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_INPUT_HIDDEN_SIZE) && + VerifyField(verifier, VT_AXIS) && verifier.EndTable(); + } + BCQGatherOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BCQGatherOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BCQGatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BCQGatherOptionsBuilder +{ + typedef BCQGatherOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_input_hidden_size(int32_t input_hidden_size) + { + fbb_.AddElement(BCQGatherOptions::VT_INPUT_HIDDEN_SIZE, input_hidden_size, 0); + } + void add_axis(int32_t axis) { fbb_.AddElement(BCQGatherOptions::VT_AXIS, axis, 0); } + explicit BCQGatherOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateBCQGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, int32_t input_hidden_size = 0, + int32_t axis = 0) +{ + BCQGatherOptionsBuilder builder_(_fbb); + builder_.add_axis(axis); + builder_.add_input_hidden_size(input_hidden_size); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBCQGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, const BCQGatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BCQFullyConnectedOptionsT : public flatbuffers::NativeTable +{ + typedef BCQFullyConnectedOptions TableType; + int32_t weights_hidden_size = 0; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct BCQFullyConnectedOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BCQFullyConnectedOptionsT NativeTableType; + typedef BCQFullyConnectedOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_WEIGHTS_HIDDEN_SIZE = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6 + }; + int32_t weights_hidden_size() const { return GetField(VT_WEIGHTS_HIDDEN_SIZE, 0); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_WEIGHTS_HIDDEN_SIZE) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + BCQFullyConnectedOptionsT * + UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BCQFullyConnectedOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BCQFullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BCQFullyConnectedOptionsBuilder +{ + typedef BCQFullyConnectedOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_weights_hidden_size(int32_t weights_hidden_size) + { + fbb_.AddElement(BCQFullyConnectedOptions::VT_WEIGHTS_HIDDEN_SIZE, weights_hidden_size, + 0); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(BCQFullyConnectedOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit BCQFullyConnectedOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBCQFullyConnectedOptions( + flatbuffers::FlatBufferBuilder &_fbb, int32_t weights_hidden_size = 0, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + BCQFullyConnectedOptionsBuilder builder_(_fbb); + builder_.add_weights_hidden_size(weights_hidden_size); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateBCQFullyConnectedOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BCQFullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct InstanceNormOptionsT : public flatbuffers::NativeTable +{ + typedef InstanceNormOptions TableType; + float epsilon = 0.0f; + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE; +}; + +struct InstanceNormOptions FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef InstanceNormOptionsT NativeTableType; + typedef InstanceNormOptionsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_EPSILON = 4, + VT_FUSED_ACTIVATION_FUNCTION = 6 + }; + float epsilon() const { return GetField(VT_EPSILON, 0.0f); } + circle::ActivationFunctionType fused_activation_function() const + { + return static_cast( + GetField(VT_FUSED_ACTIVATION_FUNCTION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_EPSILON) && + VerifyField(verifier, VT_FUSED_ACTIVATION_FUNCTION) && verifier.EndTable(); + } + InstanceNormOptionsT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(InstanceNormOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const InstanceNormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct InstanceNormOptionsBuilder +{ + typedef InstanceNormOptions Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_epsilon(float epsilon) + { + fbb_.AddElement(InstanceNormOptions::VT_EPSILON, epsilon, 0.0f); + } + void add_fused_activation_function(circle::ActivationFunctionType fused_activation_function) + { + fbb_.AddElement(InstanceNormOptions::VT_FUSED_ACTIVATION_FUNCTION, + static_cast(fused_activation_function), 0); + } + explicit InstanceNormOptionsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateInstanceNormOptions( + flatbuffers::FlatBufferBuilder &_fbb, float epsilon = 0.0f, + circle::ActivationFunctionType fused_activation_function = circle::ActivationFunctionType_NONE) +{ + InstanceNormOptionsBuilder builder_(_fbb); + builder_.add_epsilon(epsilon); + builder_.add_fused_activation_function(fused_activation_function); + return builder_.Finish(); +} + +flatbuffers::Offset +CreateInstanceNormOptions(flatbuffers::FlatBufferBuilder &_fbb, const InstanceNormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct OperatorCodeT : public flatbuffers::NativeTable +{ + typedef OperatorCode TableType; + int8_t deprecated_builtin_code = 0; + std::string custom_code{}; + int32_t version = 1; + circle::BuiltinOperator builtin_code = circle::BuiltinOperator_ADD; +}; + +struct OperatorCode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef OperatorCodeT NativeTableType; + typedef OperatorCodeBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_DEPRECATED_BUILTIN_CODE = 4, + VT_CUSTOM_CODE = 6, + VT_VERSION = 8, + VT_BUILTIN_CODE = 10 + }; + int8_t deprecated_builtin_code() const { return GetField(VT_DEPRECATED_BUILTIN_CODE, 0); } + const flatbuffers::String *custom_code() const + { + return GetPointer(VT_CUSTOM_CODE); + } + int32_t version() const { return GetField(VT_VERSION, 1); } + circle::BuiltinOperator builtin_code() const + { + return static_cast(GetField(VT_BUILTIN_CODE, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_DEPRECATED_BUILTIN_CODE) && + VerifyOffset(verifier, VT_CUSTOM_CODE) && verifier.VerifyString(custom_code()) && + VerifyField(verifier, VT_VERSION) && + VerifyField(verifier, VT_BUILTIN_CODE) && verifier.EndTable(); + } + OperatorCodeT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(OperatorCodeT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const OperatorCodeT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct OperatorCodeBuilder +{ + typedef OperatorCode Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_deprecated_builtin_code(int8_t deprecated_builtin_code) + { + fbb_.AddElement(OperatorCode::VT_DEPRECATED_BUILTIN_CODE, deprecated_builtin_code, 0); + } + void add_custom_code(flatbuffers::Offset custom_code) + { + fbb_.AddOffset(OperatorCode::VT_CUSTOM_CODE, custom_code); + } + void add_version(int32_t version) + { + fbb_.AddElement(OperatorCode::VT_VERSION, version, 1); + } + void add_builtin_code(circle::BuiltinOperator builtin_code) + { + fbb_.AddElement(OperatorCode::VT_BUILTIN_CODE, static_cast(builtin_code), 0); + } + explicit OperatorCodeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateOperatorCode(flatbuffers::FlatBufferBuilder &_fbb, int8_t deprecated_builtin_code = 0, + flatbuffers::Offset custom_code = 0, int32_t version = 1, + circle::BuiltinOperator builtin_code = circle::BuiltinOperator_ADD) +{ + OperatorCodeBuilder builder_(_fbb); + builder_.add_builtin_code(builtin_code); + builder_.add_version(version); + builder_.add_custom_code(custom_code); + builder_.add_deprecated_builtin_code(deprecated_builtin_code); + return builder_.Finish(); +} + +inline flatbuffers::Offset +CreateOperatorCodeDirect(flatbuffers::FlatBufferBuilder &_fbb, int8_t deprecated_builtin_code = 0, + const char *custom_code = nullptr, int32_t version = 1, + circle::BuiltinOperator builtin_code = circle::BuiltinOperator_ADD) +{ + auto custom_code__ = custom_code ? _fbb.CreateString(custom_code) : 0; + return circle::CreateOperatorCode(_fbb, deprecated_builtin_code, custom_code__, version, + builtin_code); +} + +flatbuffers::Offset +CreateOperatorCode(flatbuffers::FlatBufferBuilder &_fbb, const OperatorCodeT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct OperatorT : public flatbuffers::NativeTable +{ + typedef Operator TableType; + uint32_t opcode_index = 0; + std::vector inputs{}; + std::vector outputs{}; + circle::BuiltinOptionsUnion builtin_options{}; + std::vector custom_options{}; + circle::CustomOptionsFormat custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS; + std::vector mutating_variable_inputs{}; + std::vector intermediates{}; +}; + +struct Operator FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef OperatorT NativeTableType; + typedef OperatorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_OPCODE_INDEX = 4, + VT_INPUTS = 6, + VT_OUTPUTS = 8, + VT_BUILTIN_OPTIONS_TYPE = 10, + VT_BUILTIN_OPTIONS = 12, + VT_CUSTOM_OPTIONS = 14, + VT_CUSTOM_OPTIONS_FORMAT = 16, + VT_MUTATING_VARIABLE_INPUTS = 18, + VT_INTERMEDIATES = 20 + }; + uint32_t opcode_index() const { return GetField(VT_OPCODE_INDEX, 0); } + const flatbuffers::Vector *inputs() const + { + return GetPointer *>(VT_INPUTS); + } + const flatbuffers::Vector *outputs() const + { + return GetPointer *>(VT_OUTPUTS); + } + circle::BuiltinOptions builtin_options_type() const + { + return static_cast(GetField(VT_BUILTIN_OPTIONS_TYPE, 0)); + } + const void *builtin_options() const { return GetPointer(VT_BUILTIN_OPTIONS); } + template const T *builtin_options_as() const; + const circle::Conv2DOptions *builtin_options_as_Conv2DOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_Conv2DOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::DepthwiseConv2DOptions *builtin_options_as_DepthwiseConv2DOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_DepthwiseConv2DOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ConcatEmbeddingsOptions *builtin_options_as_ConcatEmbeddingsOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ConcatEmbeddingsOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LSHProjectionOptions *builtin_options_as_LSHProjectionOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LSHProjectionOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::Pool2DOptions *builtin_options_as_Pool2DOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_Pool2DOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SVDFOptions *builtin_options_as_SVDFOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SVDFOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::RNNOptions *builtin_options_as_RNNOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_RNNOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::FullyConnectedOptions *builtin_options_as_FullyConnectedOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_FullyConnectedOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SoftmaxOptions *builtin_options_as_SoftmaxOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SoftmaxOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ConcatenationOptions *builtin_options_as_ConcatenationOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ConcatenationOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::AddOptions *builtin_options_as_AddOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_AddOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::L2NormOptions *builtin_options_as_L2NormOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_L2NormOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LocalResponseNormalizationOptions * + builtin_options_as_LocalResponseNormalizationOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LocalResponseNormalizationOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LSTMOptions *builtin_options_as_LSTMOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LSTMOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ResizeBilinearOptions *builtin_options_as_ResizeBilinearOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ResizeBilinearOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::CallOptions *builtin_options_as_CallOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_CallOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ReshapeOptions *builtin_options_as_ReshapeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ReshapeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SkipGramOptions *builtin_options_as_SkipGramOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SkipGramOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SpaceToDepthOptions *builtin_options_as_SpaceToDepthOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SpaceToDepthOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::EmbeddingLookupSparseOptions * + builtin_options_as_EmbeddingLookupSparseOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_EmbeddingLookupSparseOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::MulOptions *builtin_options_as_MulOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_MulOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::PadOptions *builtin_options_as_PadOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_PadOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::GatherOptions *builtin_options_as_GatherOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_GatherOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BatchToSpaceNDOptions *builtin_options_as_BatchToSpaceNDOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BatchToSpaceNDOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SpaceToBatchNDOptions *builtin_options_as_SpaceToBatchNDOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SpaceToBatchNDOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::TransposeOptions *builtin_options_as_TransposeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_TransposeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ReducerOptions *builtin_options_as_ReducerOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ReducerOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SubOptions *builtin_options_as_SubOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SubOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::DivOptions *builtin_options_as_DivOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_DivOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SqueezeOptions *builtin_options_as_SqueezeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SqueezeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SequenceRNNOptions *builtin_options_as_SequenceRNNOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SequenceRNNOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::StridedSliceOptions *builtin_options_as_StridedSliceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_StridedSliceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ExpOptions *builtin_options_as_ExpOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ExpOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::TopKV2Options *builtin_options_as_TopKV2Options() const + { + return builtin_options_type() == circle::BuiltinOptions_TopKV2Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SplitOptions *builtin_options_as_SplitOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SplitOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LogSoftmaxOptions *builtin_options_as_LogSoftmaxOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LogSoftmaxOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::CastOptions *builtin_options_as_CastOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_CastOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::DequantizeOptions *builtin_options_as_DequantizeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_DequantizeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::MaximumMinimumOptions *builtin_options_as_MaximumMinimumOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_MaximumMinimumOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ArgMaxOptions *builtin_options_as_ArgMaxOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ArgMaxOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LessOptions *builtin_options_as_LessOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LessOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::NegOptions *builtin_options_as_NegOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_NegOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::PadV2Options *builtin_options_as_PadV2Options() const + { + return builtin_options_type() == circle::BuiltinOptions_PadV2Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::GreaterOptions *builtin_options_as_GreaterOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_GreaterOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::GreaterEqualOptions *builtin_options_as_GreaterEqualOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_GreaterEqualOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LessEqualOptions *builtin_options_as_LessEqualOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LessEqualOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SelectOptions *builtin_options_as_SelectOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SelectOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SliceOptions *builtin_options_as_SliceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SliceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::TransposeConvOptions *builtin_options_as_TransposeConvOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_TransposeConvOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SparseToDenseOptions *builtin_options_as_SparseToDenseOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SparseToDenseOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::TileOptions *builtin_options_as_TileOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_TileOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ExpandDimsOptions *builtin_options_as_ExpandDimsOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ExpandDimsOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::EqualOptions *builtin_options_as_EqualOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_EqualOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::NotEqualOptions *builtin_options_as_NotEqualOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_NotEqualOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ShapeOptions *builtin_options_as_ShapeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ShapeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::PowOptions *builtin_options_as_PowOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_PowOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ArgMinOptions *builtin_options_as_ArgMinOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ArgMinOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::FakeQuantOptions *builtin_options_as_FakeQuantOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_FakeQuantOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::PackOptions *builtin_options_as_PackOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_PackOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LogicalOrOptions *builtin_options_as_LogicalOrOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LogicalOrOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::OneHotOptions *builtin_options_as_OneHotOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_OneHotOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LogicalAndOptions *builtin_options_as_LogicalAndOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LogicalAndOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LogicalNotOptions *builtin_options_as_LogicalNotOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LogicalNotOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::UnpackOptions *builtin_options_as_UnpackOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_UnpackOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::FloorDivOptions *builtin_options_as_FloorDivOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_FloorDivOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SquareOptions *builtin_options_as_SquareOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SquareOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ZerosLikeOptions *builtin_options_as_ZerosLikeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ZerosLikeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::FillOptions *builtin_options_as_FillOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_FillOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BidirectionalSequenceLSTMOptions * + builtin_options_as_BidirectionalSequenceLSTMOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BidirectionalSequenceLSTMOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BidirectionalSequenceRNNOptions * + builtin_options_as_BidirectionalSequenceRNNOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BidirectionalSequenceRNNOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::UnidirectionalSequenceLSTMOptions * + builtin_options_as_UnidirectionalSequenceLSTMOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_UnidirectionalSequenceLSTMOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::FloorModOptions *builtin_options_as_FloorModOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_FloorModOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::RangeOptions *builtin_options_as_RangeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_RangeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ResizeNearestNeighborOptions * + builtin_options_as_ResizeNearestNeighborOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ResizeNearestNeighborOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::LeakyReluOptions *builtin_options_as_LeakyReluOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_LeakyReluOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SquaredDifferenceOptions *builtin_options_as_SquaredDifferenceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SquaredDifferenceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::MirrorPadOptions *builtin_options_as_MirrorPadOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_MirrorPadOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::AbsOptions *builtin_options_as_AbsOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_AbsOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SplitVOptions *builtin_options_as_SplitVOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SplitVOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::UniqueOptions *builtin_options_as_UniqueOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_UniqueOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ReverseV2Options *builtin_options_as_ReverseV2Options() const + { + return builtin_options_type() == circle::BuiltinOptions_ReverseV2Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::AddNOptions *builtin_options_as_AddNOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_AddNOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::GatherNdOptions *builtin_options_as_GatherNdOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_GatherNdOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::CosOptions *builtin_options_as_CosOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_CosOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::WhereOptions *builtin_options_as_WhereOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_WhereOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::RankOptions *builtin_options_as_RankOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_RankOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ReverseSequenceOptions *builtin_options_as_ReverseSequenceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ReverseSequenceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::MatrixDiagOptions *builtin_options_as_MatrixDiagOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_MatrixDiagOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::QuantizeOptions *builtin_options_as_QuantizeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_QuantizeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::MatrixSetDiagOptions *builtin_options_as_MatrixSetDiagOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_MatrixSetDiagOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::HardSwishOptions *builtin_options_as_HardSwishOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_HardSwishOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::IfOptions *builtin_options_as_IfOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_IfOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::WhileOptions *builtin_options_as_WhileOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_WhileOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::DepthToSpaceOptions *builtin_options_as_DepthToSpaceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_DepthToSpaceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::NonMaxSuppressionV4Options *builtin_options_as_NonMaxSuppressionV4Options() const + { + return builtin_options_type() == circle::BuiltinOptions_NonMaxSuppressionV4Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::NonMaxSuppressionV5Options *builtin_options_as_NonMaxSuppressionV5Options() const + { + return builtin_options_type() == circle::BuiltinOptions_NonMaxSuppressionV5Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ScatterNdOptions *builtin_options_as_ScatterNdOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ScatterNdOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SelectV2Options *builtin_options_as_SelectV2Options() const + { + return builtin_options_type() == circle::BuiltinOptions_SelectV2Options + ? static_cast(builtin_options()) + : nullptr; + } + const circle::DensifyOptions *builtin_options_as_DensifyOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_DensifyOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::SegmentSumOptions *builtin_options_as_SegmentSumOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_SegmentSumOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BatchMatMulOptions *builtin_options_as_BatchMatMulOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BatchMatMulOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::CumsumOptions *builtin_options_as_CumsumOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_CumsumOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::CallOnceOptions *builtin_options_as_CallOnceOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_CallOnceOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BroadcastToOptions *builtin_options_as_BroadcastToOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BroadcastToOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::Rfft2dOptions *builtin_options_as_Rfft2dOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_Rfft2dOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::Conv3DOptions *builtin_options_as_Conv3DOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_Conv3DOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::HashtableOptions *builtin_options_as_HashtableOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_HashtableOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::HashtableFindOptions *builtin_options_as_HashtableFindOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_HashtableFindOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::HashtableImportOptions *builtin_options_as_HashtableImportOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_HashtableImportOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::HashtableSizeOptions *builtin_options_as_HashtableSizeOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_HashtableSizeOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::VarHandleOptions *builtin_options_as_VarHandleOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_VarHandleOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::ReadVariableOptions *builtin_options_as_ReadVariableOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_ReadVariableOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::AssignVariableOptions *builtin_options_as_AssignVariableOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_AssignVariableOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::RandomOptions *builtin_options_as_RandomOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_RandomOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BCQGatherOptions *builtin_options_as_BCQGatherOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BCQGatherOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::BCQFullyConnectedOptions *builtin_options_as_BCQFullyConnectedOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_BCQFullyConnectedOptions + ? static_cast(builtin_options()) + : nullptr; + } + const circle::InstanceNormOptions *builtin_options_as_InstanceNormOptions() const + { + return builtin_options_type() == circle::BuiltinOptions_InstanceNormOptions + ? static_cast(builtin_options()) + : nullptr; + } + const flatbuffers::Vector *custom_options() const + { + return GetPointer *>(VT_CUSTOM_OPTIONS); + } + circle::CustomOptionsFormat custom_options_format() const + { + return static_cast(GetField(VT_CUSTOM_OPTIONS_FORMAT, 0)); + } + const flatbuffers::Vector *mutating_variable_inputs() const + { + return GetPointer *>(VT_MUTATING_VARIABLE_INPUTS); + } + const flatbuffers::Vector *intermediates() const + { + return GetPointer *>(VT_INTERMEDIATES); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_OPCODE_INDEX) && + VerifyOffset(verifier, VT_INPUTS) && verifier.VerifyVector(inputs()) && + VerifyOffset(verifier, VT_OUTPUTS) && verifier.VerifyVector(outputs()) && + VerifyField(verifier, VT_BUILTIN_OPTIONS_TYPE) && + VerifyOffset(verifier, VT_BUILTIN_OPTIONS) && + VerifyBuiltinOptions(verifier, builtin_options(), builtin_options_type()) && + VerifyOffset(verifier, VT_CUSTOM_OPTIONS) && verifier.VerifyVector(custom_options()) && + VerifyField(verifier, VT_CUSTOM_OPTIONS_FORMAT) && + VerifyOffset(verifier, VT_MUTATING_VARIABLE_INPUTS) && + verifier.VerifyVector(mutating_variable_inputs()) && + VerifyOffset(verifier, VT_INTERMEDIATES) && verifier.VerifyVector(intermediates()) && + verifier.EndTable(); + } + OperatorT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(OperatorT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const OperatorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +template <> +inline const circle::Conv2DOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_Conv2DOptions(); +} + +template <> +inline const circle::DepthwiseConv2DOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_DepthwiseConv2DOptions(); +} + +template <> +inline const circle::ConcatEmbeddingsOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ConcatEmbeddingsOptions(); +} + +template <> +inline const circle::LSHProjectionOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LSHProjectionOptions(); +} + +template <> +inline const circle::Pool2DOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_Pool2DOptions(); +} + +template <> +inline const circle::SVDFOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SVDFOptions(); +} + +template <> +inline const circle::RNNOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_RNNOptions(); +} + +template <> +inline const circle::FullyConnectedOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_FullyConnectedOptions(); +} + +template <> +inline const circle::SoftmaxOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SoftmaxOptions(); +} + +template <> +inline const circle::ConcatenationOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ConcatenationOptions(); +} + +template <> +inline const circle::AddOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_AddOptions(); +} + +template <> +inline const circle::L2NormOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_L2NormOptions(); +} + +template <> +inline const circle::LocalResponseNormalizationOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LocalResponseNormalizationOptions(); +} + +template <> +inline const circle::LSTMOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_LSTMOptions(); +} + +template <> +inline const circle::ResizeBilinearOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ResizeBilinearOptions(); +} + +template <> +inline const circle::CallOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_CallOptions(); +} + +template <> +inline const circle::ReshapeOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ReshapeOptions(); +} + +template <> +inline const circle::SkipGramOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SkipGramOptions(); +} + +template <> +inline const circle::SpaceToDepthOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SpaceToDepthOptions(); +} + +template <> +inline const circle::EmbeddingLookupSparseOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_EmbeddingLookupSparseOptions(); +} + +template <> +inline const circle::MulOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_MulOptions(); +} + +template <> +inline const circle::PadOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_PadOptions(); +} + +template <> +inline const circle::GatherOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_GatherOptions(); +} + +template <> +inline const circle::BatchToSpaceNDOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BatchToSpaceNDOptions(); +} + +template <> +inline const circle::SpaceToBatchNDOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SpaceToBatchNDOptions(); +} + +template <> +inline const circle::TransposeOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_TransposeOptions(); +} + +template <> +inline const circle::ReducerOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ReducerOptions(); +} + +template <> +inline const circle::SubOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SubOptions(); +} + +template <> +inline const circle::DivOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_DivOptions(); +} + +template <> +inline const circle::SqueezeOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SqueezeOptions(); +} + +template <> +inline const circle::SequenceRNNOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SequenceRNNOptions(); +} + +template <> +inline const circle::StridedSliceOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_StridedSliceOptions(); +} + +template <> +inline const circle::ExpOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ExpOptions(); +} + +template <> +inline const circle::TopKV2Options *Operator::builtin_options_as() const +{ + return builtin_options_as_TopKV2Options(); +} + +template <> +inline const circle::SplitOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SplitOptions(); +} + +template <> +inline const circle::LogSoftmaxOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LogSoftmaxOptions(); +} + +template <> +inline const circle::CastOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_CastOptions(); +} + +template <> +inline const circle::DequantizeOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_DequantizeOptions(); +} + +template <> +inline const circle::MaximumMinimumOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_MaximumMinimumOptions(); +} + +template <> +inline const circle::ArgMaxOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ArgMaxOptions(); +} + +template <> +inline const circle::LessOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_LessOptions(); +} + +template <> +inline const circle::NegOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_NegOptions(); +} + +template <> +inline const circle::PadV2Options *Operator::builtin_options_as() const +{ + return builtin_options_as_PadV2Options(); +} + +template <> +inline const circle::GreaterOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_GreaterOptions(); +} + +template <> +inline const circle::GreaterEqualOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_GreaterEqualOptions(); +} + +template <> +inline const circle::LessEqualOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LessEqualOptions(); +} + +template <> +inline const circle::SelectOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SelectOptions(); +} + +template <> +inline const circle::SliceOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SliceOptions(); +} + +template <> +inline const circle::TransposeConvOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_TransposeConvOptions(); +} + +template <> +inline const circle::SparseToDenseOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SparseToDenseOptions(); +} + +template <> +inline const circle::TileOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_TileOptions(); +} + +template <> +inline const circle::ExpandDimsOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ExpandDimsOptions(); +} + +template <> +inline const circle::EqualOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_EqualOptions(); +} + +template <> +inline const circle::NotEqualOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_NotEqualOptions(); +} + +template <> +inline const circle::ShapeOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ShapeOptions(); +} + +template <> +inline const circle::PowOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_PowOptions(); +} + +template <> +inline const circle::ArgMinOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_ArgMinOptions(); +} + +template <> +inline const circle::FakeQuantOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_FakeQuantOptions(); +} + +template <> +inline const circle::PackOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_PackOptions(); +} + +template <> +inline const circle::LogicalOrOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LogicalOrOptions(); +} + +template <> +inline const circle::OneHotOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_OneHotOptions(); +} + +template <> +inline const circle::LogicalAndOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LogicalAndOptions(); +} + +template <> +inline const circle::LogicalNotOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LogicalNotOptions(); +} + +template <> +inline const circle::UnpackOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_UnpackOptions(); +} + +template <> +inline const circle::FloorDivOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_FloorDivOptions(); +} + +template <> +inline const circle::SquareOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SquareOptions(); +} + +template <> +inline const circle::ZerosLikeOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ZerosLikeOptions(); +} + +template <> +inline const circle::FillOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_FillOptions(); +} + +template <> +inline const circle::BidirectionalSequenceLSTMOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BidirectionalSequenceLSTMOptions(); +} + +template <> +inline const circle::BidirectionalSequenceRNNOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BidirectionalSequenceRNNOptions(); +} + +template <> +inline const circle::UnidirectionalSequenceLSTMOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_UnidirectionalSequenceLSTMOptions(); +} + +template <> +inline const circle::FloorModOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_FloorModOptions(); +} + +template <> +inline const circle::RangeOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_RangeOptions(); +} + +template <> +inline const circle::ResizeNearestNeighborOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ResizeNearestNeighborOptions(); +} + +template <> +inline const circle::LeakyReluOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_LeakyReluOptions(); +} + +template <> +inline const circle::SquaredDifferenceOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SquaredDifferenceOptions(); +} + +template <> +inline const circle::MirrorPadOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_MirrorPadOptions(); +} + +template <> +inline const circle::AbsOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_AbsOptions(); +} + +template <> +inline const circle::SplitVOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_SplitVOptions(); +} + +template <> +inline const circle::UniqueOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_UniqueOptions(); +} + +template <> +inline const circle::ReverseV2Options * +Operator::builtin_options_as() const +{ + return builtin_options_as_ReverseV2Options(); +} + +template <> +inline const circle::AddNOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_AddNOptions(); +} + +template <> +inline const circle::GatherNdOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_GatherNdOptions(); +} + +template <> +inline const circle::CosOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_CosOptions(); +} + +template <> +inline const circle::WhereOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_WhereOptions(); +} + +template <> +inline const circle::RankOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_RankOptions(); +} + +template <> +inline const circle::ReverseSequenceOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ReverseSequenceOptions(); +} + +template <> +inline const circle::MatrixDiagOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_MatrixDiagOptions(); +} + +template <> +inline const circle::QuantizeOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_QuantizeOptions(); +} + +template <> +inline const circle::MatrixSetDiagOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_MatrixSetDiagOptions(); +} + +template <> +inline const circle::HardSwishOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_HardSwishOptions(); +} + +template <> inline const circle::IfOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_IfOptions(); +} + +template <> +inline const circle::WhileOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_WhileOptions(); +} + +template <> +inline const circle::DepthToSpaceOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_DepthToSpaceOptions(); +} + +template <> +inline const circle::NonMaxSuppressionV4Options * +Operator::builtin_options_as() const +{ + return builtin_options_as_NonMaxSuppressionV4Options(); +} + +template <> +inline const circle::NonMaxSuppressionV5Options * +Operator::builtin_options_as() const +{ + return builtin_options_as_NonMaxSuppressionV5Options(); +} + +template <> +inline const circle::ScatterNdOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ScatterNdOptions(); +} + +template <> +inline const circle::SelectV2Options *Operator::builtin_options_as() const +{ + return builtin_options_as_SelectV2Options(); +} + +template <> +inline const circle::DensifyOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_DensifyOptions(); +} + +template <> +inline const circle::SegmentSumOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_SegmentSumOptions(); +} + +template <> +inline const circle::BatchMatMulOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BatchMatMulOptions(); +} + +template <> +inline const circle::CumsumOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_CumsumOptions(); +} + +template <> +inline const circle::CallOnceOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_CallOnceOptions(); +} + +template <> +inline const circle::BroadcastToOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BroadcastToOptions(); +} + +template <> +inline const circle::Rfft2dOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_Rfft2dOptions(); +} + +template <> +inline const circle::Conv3DOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_Conv3DOptions(); +} + +template <> +inline const circle::HashtableOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_HashtableOptions(); +} + +template <> +inline const circle::HashtableFindOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_HashtableFindOptions(); +} + +template <> +inline const circle::HashtableImportOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_HashtableImportOptions(); +} + +template <> +inline const circle::HashtableSizeOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_HashtableSizeOptions(); +} + +template <> +inline const circle::VarHandleOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_VarHandleOptions(); +} + +template <> +inline const circle::ReadVariableOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_ReadVariableOptions(); +} + +template <> +inline const circle::AssignVariableOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_AssignVariableOptions(); +} + +template <> +inline const circle::RandomOptions *Operator::builtin_options_as() const +{ + return builtin_options_as_RandomOptions(); +} + +template <> +inline const circle::BCQGatherOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BCQGatherOptions(); +} + +template <> +inline const circle::BCQFullyConnectedOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_BCQFullyConnectedOptions(); +} + +template <> +inline const circle::InstanceNormOptions * +Operator::builtin_options_as() const +{ + return builtin_options_as_InstanceNormOptions(); +} + +struct OperatorBuilder +{ + typedef Operator Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_opcode_index(uint32_t opcode_index) + { + fbb_.AddElement(Operator::VT_OPCODE_INDEX, opcode_index, 0); + } + void add_inputs(flatbuffers::Offset> inputs) + { + fbb_.AddOffset(Operator::VT_INPUTS, inputs); + } + void add_outputs(flatbuffers::Offset> outputs) + { + fbb_.AddOffset(Operator::VT_OUTPUTS, outputs); + } + void add_builtin_options_type(circle::BuiltinOptions builtin_options_type) + { + fbb_.AddElement(Operator::VT_BUILTIN_OPTIONS_TYPE, + static_cast(builtin_options_type), 0); + } + void add_builtin_options(flatbuffers::Offset builtin_options) + { + fbb_.AddOffset(Operator::VT_BUILTIN_OPTIONS, builtin_options); + } + void add_custom_options(flatbuffers::Offset> custom_options) + { + fbb_.AddOffset(Operator::VT_CUSTOM_OPTIONS, custom_options); + } + void add_custom_options_format(circle::CustomOptionsFormat custom_options_format) + { + fbb_.AddElement(Operator::VT_CUSTOM_OPTIONS_FORMAT, + static_cast(custom_options_format), 0); + } + void add_mutating_variable_inputs( + flatbuffers::Offset> mutating_variable_inputs) + { + fbb_.AddOffset(Operator::VT_MUTATING_VARIABLE_INPUTS, mutating_variable_inputs); + } + void add_intermediates(flatbuffers::Offset> intermediates) + { + fbb_.AddOffset(Operator::VT_INTERMEDIATES, intermediates); + } + explicit OperatorBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateOperator( + flatbuffers::FlatBufferBuilder &_fbb, uint32_t opcode_index = 0, + flatbuffers::Offset> inputs = 0, + flatbuffers::Offset> outputs = 0, + circle::BuiltinOptions builtin_options_type = circle::BuiltinOptions_NONE, + flatbuffers::Offset builtin_options = 0, + flatbuffers::Offset> custom_options = 0, + circle::CustomOptionsFormat custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS, + flatbuffers::Offset> mutating_variable_inputs = 0, + flatbuffers::Offset> intermediates = 0) +{ + OperatorBuilder builder_(_fbb); + builder_.add_intermediates(intermediates); + builder_.add_mutating_variable_inputs(mutating_variable_inputs); + builder_.add_custom_options(custom_options); + builder_.add_builtin_options(builtin_options); + builder_.add_outputs(outputs); + builder_.add_inputs(inputs); + builder_.add_opcode_index(opcode_index); + builder_.add_custom_options_format(custom_options_format); + builder_.add_builtin_options_type(builtin_options_type); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateOperatorDirect( + flatbuffers::FlatBufferBuilder &_fbb, uint32_t opcode_index = 0, + const std::vector *inputs = nullptr, const std::vector *outputs = nullptr, + circle::BuiltinOptions builtin_options_type = circle::BuiltinOptions_NONE, + flatbuffers::Offset builtin_options = 0, + const std::vector *custom_options = nullptr, + circle::CustomOptionsFormat custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS, + const std::vector *mutating_variable_inputs = nullptr, + const std::vector *intermediates = nullptr) +{ + auto inputs__ = inputs ? _fbb.CreateVector(*inputs) : 0; + auto outputs__ = outputs ? _fbb.CreateVector(*outputs) : 0; + auto custom_options__ = custom_options ? _fbb.CreateVector(*custom_options) : 0; + auto mutating_variable_inputs__ = + mutating_variable_inputs ? _fbb.CreateVector(*mutating_variable_inputs) : 0; + auto intermediates__ = intermediates ? _fbb.CreateVector(*intermediates) : 0; + return circle::CreateOperator(_fbb, opcode_index, inputs__, outputs__, builtin_options_type, + builtin_options, custom_options__, custom_options_format, + mutating_variable_inputs__, intermediates__); +} + +flatbuffers::Offset +CreateOperator(flatbuffers::FlatBufferBuilder &_fbb, const OperatorT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SubGraphT : public flatbuffers::NativeTable +{ + typedef SubGraph TableType; + std::vector> tensors{}; + std::vector inputs{}; + std::vector outputs{}; + std::vector> operators{}; + std::string name{}; + circle::DataFormat data_format = circle::DataFormat_CHANNELS_LAST; +}; + +struct SubGraph FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SubGraphT NativeTableType; + typedef SubGraphBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_TENSORS = 4, + VT_INPUTS = 6, + VT_OUTPUTS = 8, + VT_OPERATORS = 10, + VT_NAME = 12, + VT_DATA_FORMAT = 14 + }; + const flatbuffers::Vector> *tensors() const + { + return GetPointer> *>(VT_TENSORS); + } + const flatbuffers::Vector *inputs() const + { + return GetPointer *>(VT_INPUTS); + } + const flatbuffers::Vector *outputs() const + { + return GetPointer *>(VT_OUTPUTS); + } + const flatbuffers::Vector> *operators() const + { + return GetPointer> *>( + VT_OPERATORS); + } + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + circle::DataFormat data_format() const + { + return static_cast(GetField(VT_DATA_FORMAT, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_TENSORS) && + verifier.VerifyVector(tensors()) && verifier.VerifyVectorOfTables(tensors()) && + VerifyOffset(verifier, VT_INPUTS) && verifier.VerifyVector(inputs()) && + VerifyOffset(verifier, VT_OUTPUTS) && verifier.VerifyVector(outputs()) && + VerifyOffset(verifier, VT_OPERATORS) && verifier.VerifyVector(operators()) && + verifier.VerifyVectorOfTables(operators()) && VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyField(verifier, VT_DATA_FORMAT) && + verifier.EndTable(); + } + SubGraphT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SubGraphT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SubGraphT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SubGraphBuilder +{ + typedef SubGraph Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void + add_tensors(flatbuffers::Offset>> tensors) + { + fbb_.AddOffset(SubGraph::VT_TENSORS, tensors); + } + void add_inputs(flatbuffers::Offset> inputs) + { + fbb_.AddOffset(SubGraph::VT_INPUTS, inputs); + } + void add_outputs(flatbuffers::Offset> outputs) + { + fbb_.AddOffset(SubGraph::VT_OUTPUTS, outputs); + } + void add_operators( + flatbuffers::Offset>> operators) + { + fbb_.AddOffset(SubGraph::VT_OPERATORS, operators); + } + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(SubGraph::VT_NAME, name); + } + void add_data_format(circle::DataFormat data_format) + { + fbb_.AddElement(SubGraph::VT_DATA_FORMAT, static_cast(data_format), 0); + } + explicit SubGraphBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSubGraph( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> tensors = 0, + flatbuffers::Offset> inputs = 0, + flatbuffers::Offset> outputs = 0, + flatbuffers::Offset>> operators = 0, + flatbuffers::Offset name = 0, + circle::DataFormat data_format = circle::DataFormat_CHANNELS_LAST) +{ + SubGraphBuilder builder_(_fbb); + builder_.add_name(name); + builder_.add_operators(operators); + builder_.add_outputs(outputs); + builder_.add_inputs(inputs); + builder_.add_tensors(tensors); + builder_.add_data_format(data_format); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSubGraphDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector> *tensors = nullptr, + const std::vector *inputs = nullptr, const std::vector *outputs = nullptr, + const std::vector> *operators = nullptr, + const char *name = nullptr, circle::DataFormat data_format = circle::DataFormat_CHANNELS_LAST) +{ + auto tensors__ = tensors ? _fbb.CreateVector>(*tensors) : 0; + auto inputs__ = inputs ? _fbb.CreateVector(*inputs) : 0; + auto outputs__ = outputs ? _fbb.CreateVector(*outputs) : 0; + auto operators__ = + operators ? _fbb.CreateVector>(*operators) : 0; + auto name__ = name ? _fbb.CreateString(name) : 0; + return circle::CreateSubGraph(_fbb, tensors__, inputs__, outputs__, operators__, name__, + data_format); +} + +flatbuffers::Offset +CreateSubGraph(flatbuffers::FlatBufferBuilder &_fbb, const SubGraphT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct BufferT : public flatbuffers::NativeTable +{ + typedef Buffer TableType; + std::vector data{}; +}; + +struct Buffer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef BufferT NativeTableType; + typedef BufferBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_DATA = 4 + }; + const flatbuffers::Vector *data() const + { + return GetPointer *>(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && verifier.EndTable(); + } + BufferT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(BufferT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const BufferT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct BufferBuilder +{ + typedef Buffer Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_data(flatbuffers::Offset> data) + { + fbb_.AddOffset(Buffer::VT_DATA, data); + } + explicit BufferBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateBuffer(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> data = 0) +{ + BufferBuilder builder_(_fbb); + builder_.add_data(data); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateBufferDirect(flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr) +{ + if (data) + { + _fbb.ForceVectorAlignment(data->size(), sizeof(uint8_t), 16); + } + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return circle::CreateBuffer(_fbb, data__); +} + +flatbuffers::Offset +CreateBuffer(flatbuffers::FlatBufferBuilder &_fbb, const BufferT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct MetadataT : public flatbuffers::NativeTable +{ + typedef Metadata TableType; + std::string name{}; + uint32_t buffer = 0; +}; + +struct Metadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef MetadataT NativeTableType; + typedef MetadataBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_BUFFER = 6 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + uint32_t buffer() const { return GetField(VT_BUFFER, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyField(verifier, VT_BUFFER) && + verifier.EndTable(); + } + MetadataT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(MetadataT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const MetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MetadataBuilder +{ + typedef Metadata Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(Metadata::VT_NAME, name); + } + void add_buffer(uint32_t buffer) { fbb_.AddElement(Metadata::VT_BUFFER, buffer, 0); } + explicit MetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateMetadata(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset name = 0, uint32_t buffer = 0) +{ + MetadataBuilder builder_(_fbb); + builder_.add_buffer(buffer); + builder_.add_name(name); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateMetadataDirect(flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + uint32_t buffer = 0) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + return circle::CreateMetadata(_fbb, name__, buffer); +} + +flatbuffers::Offset +CreateMetadata(flatbuffers::FlatBufferBuilder &_fbb, const MetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TensorMapT : public flatbuffers::NativeTable +{ + typedef TensorMap TableType; + std::string name{}; + uint32_t tensor_index = 0; +}; + +struct TensorMap FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef TensorMapT NativeTableType; + typedef TensorMapBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_NAME = 4, + VT_TENSOR_INDEX = 6 + }; + const flatbuffers::String *name() const + { + return GetPointer(VT_NAME); + } + uint32_t tensor_index() const { return GetField(VT_TENSOR_INDEX, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && + verifier.VerifyString(name()) && VerifyField(verifier, VT_TENSOR_INDEX) && + verifier.EndTable(); + } + TensorMapT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TensorMapT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const TensorMapT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TensorMapBuilder +{ + typedef TensorMap Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_name(flatbuffers::Offset name) + { + fbb_.AddOffset(TensorMap::VT_NAME, name); + } + void add_tensor_index(uint32_t tensor_index) + { + fbb_.AddElement(TensorMap::VT_TENSOR_INDEX, tensor_index, 0); + } + explicit TensorMapBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset +CreateTensorMap(flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset name = 0, uint32_t tensor_index = 0) +{ + TensorMapBuilder builder_(_fbb); + builder_.add_tensor_index(tensor_index); + builder_.add_name(name); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTensorMapDirect(flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + uint32_t tensor_index = 0) +{ + auto name__ = name ? _fbb.CreateString(name) : 0; + return circle::CreateTensorMap(_fbb, name__, tensor_index); +} + +flatbuffers::Offset +CreateTensorMap(flatbuffers::FlatBufferBuilder &_fbb, const TensorMapT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct SignatureDefT : public flatbuffers::NativeTable +{ + typedef SignatureDef TableType; + std::vector> inputs{}; + std::vector> outputs{}; + std::string signature_key{}; + uint32_t subgraph_index = 0; +}; + +struct SignatureDef FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef SignatureDefT NativeTableType; + typedef SignatureDefBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_INPUTS = 4, + VT_OUTPUTS = 6, + VT_SIGNATURE_KEY = 8, + VT_SUBGRAPH_INDEX = 12 + }; + const flatbuffers::Vector> *inputs() const + { + return GetPointer> *>( + VT_INPUTS); + } + const flatbuffers::Vector> *outputs() const + { + return GetPointer> *>( + VT_OUTPUTS); + } + const flatbuffers::String *signature_key() const + { + return GetPointer(VT_SIGNATURE_KEY); + } + uint32_t subgraph_index() const { return GetField(VT_SUBGRAPH_INDEX, 0); } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_INPUTS) && + verifier.VerifyVector(inputs()) && verifier.VerifyVectorOfTables(inputs()) && + VerifyOffset(verifier, VT_OUTPUTS) && verifier.VerifyVector(outputs()) && + verifier.VerifyVectorOfTables(outputs()) && VerifyOffset(verifier, VT_SIGNATURE_KEY) && + verifier.VerifyString(signature_key()) && + VerifyField(verifier, VT_SUBGRAPH_INDEX) && verifier.EndTable(); + } + SignatureDefT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(SignatureDefT *_o, + const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const SignatureDefT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct SignatureDefBuilder +{ + typedef SignatureDef Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_inputs( + flatbuffers::Offset>> inputs) + { + fbb_.AddOffset(SignatureDef::VT_INPUTS, inputs); + } + void add_outputs( + flatbuffers::Offset>> outputs) + { + fbb_.AddOffset(SignatureDef::VT_OUTPUTS, outputs); + } + void add_signature_key(flatbuffers::Offset signature_key) + { + fbb_.AddOffset(SignatureDef::VT_SIGNATURE_KEY, signature_key); + } + void add_subgraph_index(uint32_t subgraph_index) + { + fbb_.AddElement(SignatureDef::VT_SUBGRAPH_INDEX, subgraph_index, 0); + } + explicit SignatureDefBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSignatureDef( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> inputs = 0, + flatbuffers::Offset>> outputs = 0, + flatbuffers::Offset signature_key = 0, uint32_t subgraph_index = 0) +{ + SignatureDefBuilder builder_(_fbb); + builder_.add_subgraph_index(subgraph_index); + builder_.add_signature_key(signature_key); + builder_.add_outputs(outputs); + builder_.add_inputs(inputs); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSignatureDefDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector> *inputs = nullptr, + const std::vector> *outputs = nullptr, + const char *signature_key = nullptr, uint32_t subgraph_index = 0) +{ + auto inputs__ = inputs ? _fbb.CreateVector>(*inputs) : 0; + auto outputs__ = + outputs ? _fbb.CreateVector>(*outputs) : 0; + auto signature_key__ = signature_key ? _fbb.CreateString(signature_key) : 0; + return circle::CreateSignatureDef(_fbb, inputs__, outputs__, signature_key__, subgraph_index); +} + +flatbuffers::Offset +CreateSignatureDef(flatbuffers::FlatBufferBuilder &_fbb, const SignatureDefT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct ModelT : public flatbuffers::NativeTable +{ + typedef Model TableType; + uint32_t version = 0; + std::vector> operator_codes{}; + std::vector> subgraphs{}; + std::string description{}; + std::vector> buffers{}; + std::vector metadata_buffer{}; + std::vector> metadata{}; + std::vector> signature_defs{}; +}; + +struct Model FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table +{ + typedef ModelT NativeTableType; + typedef ModelBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE + { + VT_VERSION = 4, + VT_OPERATOR_CODES = 6, + VT_SUBGRAPHS = 8, + VT_DESCRIPTION = 10, + VT_BUFFERS = 12, + VT_METADATA_BUFFER = 14, + VT_METADATA = 16, + VT_SIGNATURE_DEFS = 18 + }; + uint32_t version() const { return GetField(VT_VERSION, 0); } + const flatbuffers::Vector> *operator_codes() const + { + return GetPointer> *>( + VT_OPERATOR_CODES); + } + const flatbuffers::Vector> *subgraphs() const + { + return GetPointer> *>( + VT_SUBGRAPHS); + } + const flatbuffers::String *description() const + { + return GetPointer(VT_DESCRIPTION); + } + const flatbuffers::Vector> *buffers() const + { + return GetPointer> *>(VT_BUFFERS); + } + const flatbuffers::Vector *metadata_buffer() const + { + return GetPointer *>(VT_METADATA_BUFFER); + } + const flatbuffers::Vector> *metadata() const + { + return GetPointer> *>( + VT_METADATA); + } + const flatbuffers::Vector> *signature_defs() const + { + return GetPointer> *>( + VT_SIGNATURE_DEFS); + } + bool Verify(flatbuffers::Verifier &verifier) const + { + return VerifyTableStart(verifier) && VerifyField(verifier, VT_VERSION) && + VerifyOffset(verifier, VT_OPERATOR_CODES) && verifier.VerifyVector(operator_codes()) && + verifier.VerifyVectorOfTables(operator_codes()) && + VerifyOffset(verifier, VT_SUBGRAPHS) && verifier.VerifyVector(subgraphs()) && + verifier.VerifyVectorOfTables(subgraphs()) && VerifyOffset(verifier, VT_DESCRIPTION) && + verifier.VerifyString(description()) && VerifyOffset(verifier, VT_BUFFERS) && + verifier.VerifyVector(buffers()) && verifier.VerifyVectorOfTables(buffers()) && + VerifyOffset(verifier, VT_METADATA_BUFFER) && verifier.VerifyVector(metadata_buffer()) && + VerifyOffset(verifier, VT_METADATA) && verifier.VerifyVector(metadata()) && + verifier.VerifyVectorOfTables(metadata()) && VerifyOffset(verifier, VT_SIGNATURE_DEFS) && + verifier.VerifyVector(signature_defs()) && + verifier.VerifyVectorOfTables(signature_defs()) && verifier.EndTable(); + } + ModelT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(ModelT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset + Pack(flatbuffers::FlatBufferBuilder &_fbb, const ModelT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct ModelBuilder +{ + typedef Model Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_version(uint32_t version) { fbb_.AddElement(Model::VT_VERSION, version, 0); } + void add_operator_codes( + flatbuffers::Offset>> + operator_codes) + { + fbb_.AddOffset(Model::VT_OPERATOR_CODES, operator_codes); + } + void add_subgraphs( + flatbuffers::Offset>> subgraphs) + { + fbb_.AddOffset(Model::VT_SUBGRAPHS, subgraphs); + } + void add_description(flatbuffers::Offset description) + { + fbb_.AddOffset(Model::VT_DESCRIPTION, description); + } + void + add_buffers(flatbuffers::Offset>> buffers) + { + fbb_.AddOffset(Model::VT_BUFFERS, buffers); + } + void add_metadata_buffer(flatbuffers::Offset> metadata_buffer) + { + fbb_.AddOffset(Model::VT_METADATA_BUFFER, metadata_buffer); + } + void add_metadata( + flatbuffers::Offset>> metadata) + { + fbb_.AddOffset(Model::VT_METADATA, metadata); + } + void add_signature_defs( + flatbuffers::Offset>> + signature_defs) + { + fbb_.AddOffset(Model::VT_SIGNATURE_DEFS, signature_defs); + } + explicit ModelBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) + { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() + { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateModel( + flatbuffers::FlatBufferBuilder &_fbb, uint32_t version = 0, + flatbuffers::Offset>> + operator_codes = 0, + flatbuffers::Offset>> subgraphs = 0, + flatbuffers::Offset description = 0, + flatbuffers::Offset>> buffers = 0, + flatbuffers::Offset> metadata_buffer = 0, + flatbuffers::Offset>> metadata = 0, + flatbuffers::Offset>> + signature_defs = 0) +{ + ModelBuilder builder_(_fbb); + builder_.add_signature_defs(signature_defs); + builder_.add_metadata(metadata); + builder_.add_metadata_buffer(metadata_buffer); + builder_.add_buffers(buffers); + builder_.add_description(description); + builder_.add_subgraphs(subgraphs); + builder_.add_operator_codes(operator_codes); + builder_.add_version(version); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateModelDirect( + flatbuffers::FlatBufferBuilder &_fbb, uint32_t version = 0, + const std::vector> *operator_codes = nullptr, + const std::vector> *subgraphs = nullptr, + const char *description = nullptr, + const std::vector> *buffers = nullptr, + const std::vector *metadata_buffer = nullptr, + const std::vector> *metadata = nullptr, + const std::vector> *signature_defs = nullptr) +{ + auto operator_codes__ = + operator_codes ? _fbb.CreateVector>(*operator_codes) + : 0; + auto subgraphs__ = + subgraphs ? _fbb.CreateVector>(*subgraphs) : 0; + auto description__ = description ? _fbb.CreateString(description) : 0; + auto buffers__ = buffers ? _fbb.CreateVector>(*buffers) : 0; + auto metadata_buffer__ = metadata_buffer ? _fbb.CreateVector(*metadata_buffer) : 0; + auto metadata__ = + metadata ? _fbb.CreateVector>(*metadata) : 0; + auto signature_defs__ = + signature_defs ? _fbb.CreateVector>(*signature_defs) + : 0; + return circle::CreateModel(_fbb, version, operator_codes__, subgraphs__, description__, buffers__, + metadata_buffer__, metadata__, signature_defs__); +} + +flatbuffers::Offset CreateModel(flatbuffers::FlatBufferBuilder &_fbb, const ModelT *_o, + const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +inline CustomQuantizationT * +CustomQuantization::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CustomQuantizationT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CustomQuantization::UnPackTo(CustomQuantizationT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = custom(); + if (_e) + { + _o->custom.resize(_e->size()); + std::copy(_e->begin(), _e->end(), _o->custom.begin()); + } + } +} + +inline flatbuffers::Offset +CustomQuantization::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CustomQuantizationT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCustomQuantization(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCustomQuantization(flatbuffers::FlatBufferBuilder &_fbb, const CustomQuantizationT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CustomQuantizationT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + _fbb.ForceVectorAlignment(_o->custom.size(), sizeof(uint8_t), 16); + auto _custom = _o->custom.size() ? _fbb.CreateVector(_o->custom) : 0; + return circle::CreateCustomQuantization(_fbb, _custom); +} + +inline QuantizationParametersT * +QuantizationParameters::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new QuantizationParametersT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +QuantizationParameters::UnPackTo(QuantizationParametersT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = min(); + if (_e) + { + _o->min.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->min[_i] = _e->Get(_i); + } + } + } + { + auto _e = max(); + if (_e) + { + _o->max.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->max[_i] = _e->Get(_i); + } + } + } + { + auto _e = scale(); + if (_e) + { + _o->scale.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->scale[_i] = _e->Get(_i); + } + } + } + { + auto _e = zero_point(); + if (_e) + { + _o->zero_point.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->zero_point[_i] = _e->Get(_i); + } + } + } + { + auto _e = details_type(); + _o->details.type = _e; + } + { + auto _e = details(); + if (_e) + _o->details.value = circle::QuantizationDetailsUnion::UnPack(_e, details_type(), _resolver); + } + { + auto _e = quantized_dimension(); + _o->quantized_dimension = _e; + } +} + +inline flatbuffers::Offset +QuantizationParameters::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const QuantizationParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateQuantizationParameters(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateQuantizationParameters(flatbuffers::FlatBufferBuilder &_fbb, + const QuantizationParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const QuantizationParametersT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _min = _o->min.size() ? _fbb.CreateVector(_o->min) : 0; + auto _max = _o->max.size() ? _fbb.CreateVector(_o->max) : 0; + auto _scale = _o->scale.size() ? _fbb.CreateVector(_o->scale) : 0; + auto _zero_point = _o->zero_point.size() ? _fbb.CreateVector(_o->zero_point) : 0; + auto _details_type = _o->details.type; + auto _details = _o->details.Pack(_fbb); + auto _quantized_dimension = _o->quantized_dimension; + return circle::CreateQuantizationParameters(_fbb, _min, _max, _scale, _zero_point, _details_type, + _details, _quantized_dimension); +} + +inline Int32VectorT *Int32Vector::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Int32VectorT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Int32Vector::UnPackTo(Int32VectorT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = values(); + if (_e) + { + _o->values.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->values[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +Int32Vector::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Int32VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateInt32Vector(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateInt32Vector(flatbuffers::FlatBufferBuilder &_fbb, const Int32VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Int32VectorT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _values = _o->values.size() ? _fbb.CreateVector(_o->values) : 0; + return circle::CreateInt32Vector(_fbb, _values); +} + +inline Uint16VectorT *Uint16Vector::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Uint16VectorT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Uint16Vector::UnPackTo(Uint16VectorT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = values(); + if (_e) + { + _o->values.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->values[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +Uint16Vector::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Uint16VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateUint16Vector(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateUint16Vector(flatbuffers::FlatBufferBuilder &_fbb, const Uint16VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Uint16VectorT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + _fbb.ForceVectorAlignment(_o->values.size(), sizeof(uint16_t), 4); + auto _values = _o->values.size() ? _fbb.CreateVector(_o->values) : 0; + return circle::CreateUint16Vector(_fbb, _values); +} + +inline Uint8VectorT *Uint8Vector::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Uint8VectorT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Uint8Vector::UnPackTo(Uint8VectorT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = values(); + if (_e) + { + _o->values.resize(_e->size()); + std::copy(_e->begin(), _e->end(), _o->values.begin()); + } + } +} + +inline flatbuffers::Offset +Uint8Vector::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Uint8VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateUint8Vector(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateUint8Vector(flatbuffers::FlatBufferBuilder &_fbb, const Uint8VectorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Uint8VectorT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + _fbb.ForceVectorAlignment(_o->values.size(), sizeof(uint8_t), 4); + auto _values = _o->values.size() ? _fbb.CreateVector(_o->values) : 0; + return circle::CreateUint8Vector(_fbb, _values); +} + +inline DimensionMetadataT * +DimensionMetadata::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DimensionMetadataT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void DimensionMetadata::UnPackTo(DimensionMetadataT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = format(); + _o->format = _e; + } + { + auto _e = dense_size(); + _o->dense_size = _e; + } + { + auto _e = array_segments_type(); + _o->array_segments.type = _e; + } + { + auto _e = array_segments(); + if (_e) + _o->array_segments.value = + circle::SparseIndexVectorUnion::UnPack(_e, array_segments_type(), _resolver); + } + { + auto _e = array_indices_type(); + _o->array_indices.type = _e; + } + { + auto _e = array_indices(); + if (_e) + _o->array_indices.value = + circle::SparseIndexVectorUnion::UnPack(_e, array_indices_type(), _resolver); + } +} + +inline flatbuffers::Offset +DimensionMetadata::Pack(flatbuffers::FlatBufferBuilder &_fbb, const DimensionMetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDimensionMetadata(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDimensionMetadata(flatbuffers::FlatBufferBuilder &_fbb, const DimensionMetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DimensionMetadataT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _format = _o->format; + auto _dense_size = _o->dense_size; + auto _array_segments_type = _o->array_segments.type; + auto _array_segments = _o->array_segments.Pack(_fbb); + auto _array_indices_type = _o->array_indices.type; + auto _array_indices = _o->array_indices.Pack(_fbb); + return circle::CreateDimensionMetadata(_fbb, _format, _dense_size, _array_segments_type, + _array_segments, _array_indices_type, _array_indices); +} + +inline SparsityParametersT * +SparsityParameters::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SparsityParametersT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SparsityParameters::UnPackTo(SparsityParametersT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = traversal_order(); + if (_e) + { + _o->traversal_order.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->traversal_order[_i] = _e->Get(_i); + } + } + } + { + auto _e = block_map(); + if (_e) + { + _o->block_map.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->block_map[_i] = _e->Get(_i); + } + } + } + { + auto _e = dim_metadata(); + if (_e) + { + _o->dim_metadata.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->dim_metadata[_i] = + std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } +} + +inline flatbuffers::Offset +SparsityParameters::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SparsityParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSparsityParameters(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSparsityParameters(flatbuffers::FlatBufferBuilder &_fbb, const SparsityParametersT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SparsityParametersT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _traversal_order = _o->traversal_order.size() ? _fbb.CreateVector(_o->traversal_order) : 0; + auto _block_map = _o->block_map.size() ? _fbb.CreateVector(_o->block_map) : 0; + auto _dim_metadata = _o->dim_metadata.size() + ? _fbb.CreateVector>( + _o->dim_metadata.size(), + [](size_t i, _VectorArgs *__va) { + return CreateDimensionMetadata( + *__va->__fbb, __va->__o->dim_metadata[i].get(), __va->__rehasher); + }, + &_va) + : 0; + return circle::CreateSparsityParameters(_fbb, _traversal_order, _block_map, _dim_metadata); +} + +inline TensorT *Tensor::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TensorT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Tensor::UnPackTo(TensorT *_o, const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = shape(); + if (_e) + { + _o->shape.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->shape[_i] = _e->Get(_i); + } + } + } + { + auto _e = type(); + _o->type = _e; + } + { + auto _e = buffer(); + _o->buffer = _e; + } + { + auto _e = name(); + if (_e) + _o->name = _e->str(); + } + { + auto _e = quantization(); + if (_e) + _o->quantization = std::unique_ptr(_e->UnPack(_resolver)); + } + { + auto _e = is_variable(); + _o->is_variable = _e; + } + { + auto _e = sparsity(); + if (_e) + _o->sparsity = std::unique_ptr(_e->UnPack(_resolver)); + } + { + auto _e = shape_signature(); + if (_e) + { + _o->shape_signature.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->shape_signature[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset Tensor::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const TensorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTensor(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateTensor(flatbuffers::FlatBufferBuilder &_fbb, + const TensorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TensorT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _shape = _o->shape.size() ? _fbb.CreateVector(_o->shape) : 0; + auto _type = _o->type; + auto _buffer = _o->buffer; + auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); + auto _quantization = + _o->quantization ? CreateQuantizationParameters(_fbb, _o->quantization.get(), _rehasher) : 0; + auto _is_variable = _o->is_variable; + auto _sparsity = _o->sparsity ? CreateSparsityParameters(_fbb, _o->sparsity.get(), _rehasher) : 0; + auto _shape_signature = _o->shape_signature.size() ? _fbb.CreateVector(_o->shape_signature) : 0; + return circle::CreateTensor(_fbb, _shape, _type, _buffer, _name, _quantization, _is_variable, + _sparsity, _shape_signature); +} + +inline Conv2DOptionsT * +Conv2DOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Conv2DOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Conv2DOptions::UnPackTo(Conv2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = padding(); + _o->padding = _e; + } + { + auto _e = stride_w(); + _o->stride_w = _e; + } + { + auto _e = stride_h(); + _o->stride_h = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = dilation_w_factor(); + _o->dilation_w_factor = _e; + } + { + auto _e = dilation_h_factor(); + _o->dilation_h_factor = _e; + } +} + +inline flatbuffers::Offset +Conv2DOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Conv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateConv2DOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateConv2DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Conv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Conv2DOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _padding = _o->padding; + auto _stride_w = _o->stride_w; + auto _stride_h = _o->stride_h; + auto _fused_activation_function = _o->fused_activation_function; + auto _dilation_w_factor = _o->dilation_w_factor; + auto _dilation_h_factor = _o->dilation_h_factor; + return circle::CreateConv2DOptions(_fbb, _padding, _stride_w, _stride_h, + _fused_activation_function, _dilation_w_factor, + _dilation_h_factor); +} + +inline Conv3DOptionsT * +Conv3DOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Conv3DOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Conv3DOptions::UnPackTo(Conv3DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = padding(); + _o->padding = _e; + } + { + auto _e = stride_d(); + _o->stride_d = _e; + } + { + auto _e = stride_w(); + _o->stride_w = _e; + } + { + auto _e = stride_h(); + _o->stride_h = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = dilation_d_factor(); + _o->dilation_d_factor = _e; + } + { + auto _e = dilation_w_factor(); + _o->dilation_w_factor = _e; + } + { + auto _e = dilation_h_factor(); + _o->dilation_h_factor = _e; + } +} + +inline flatbuffers::Offset +Conv3DOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Conv3DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateConv3DOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateConv3DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Conv3DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Conv3DOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _padding = _o->padding; + auto _stride_d = _o->stride_d; + auto _stride_w = _o->stride_w; + auto _stride_h = _o->stride_h; + auto _fused_activation_function = _o->fused_activation_function; + auto _dilation_d_factor = _o->dilation_d_factor; + auto _dilation_w_factor = _o->dilation_w_factor; + auto _dilation_h_factor = _o->dilation_h_factor; + return circle::CreateConv3DOptions(_fbb, _padding, _stride_d, _stride_w, _stride_h, + _fused_activation_function, _dilation_d_factor, + _dilation_w_factor, _dilation_h_factor); +} + +inline Pool2DOptionsT * +Pool2DOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Pool2DOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Pool2DOptions::UnPackTo(Pool2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = padding(); + _o->padding = _e; + } + { + auto _e = stride_w(); + _o->stride_w = _e; + } + { + auto _e = stride_h(); + _o->stride_h = _e; + } + { + auto _e = filter_width(); + _o->filter_width = _e; + } + { + auto _e = filter_height(); + _o->filter_height = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +Pool2DOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Pool2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreatePool2DOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreatePool2DOptions(flatbuffers::FlatBufferBuilder &_fbb, const Pool2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Pool2DOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _padding = _o->padding; + auto _stride_w = _o->stride_w; + auto _stride_h = _o->stride_h; + auto _filter_width = _o->filter_width; + auto _filter_height = _o->filter_height; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreatePool2DOptions(_fbb, _padding, _stride_w, _stride_h, _filter_width, + _filter_height, _fused_activation_function); +} + +inline DepthwiseConv2DOptionsT * +DepthwiseConv2DOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DepthwiseConv2DOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +DepthwiseConv2DOptions::UnPackTo(DepthwiseConv2DOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = padding(); + _o->padding = _e; + } + { + auto _e = stride_w(); + _o->stride_w = _e; + } + { + auto _e = stride_h(); + _o->stride_h = _e; + } + { + auto _e = depth_multiplier(); + _o->depth_multiplier = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = dilation_w_factor(); + _o->dilation_w_factor = _e; + } + { + auto _e = dilation_h_factor(); + _o->dilation_h_factor = _e; + } +} + +inline flatbuffers::Offset +DepthwiseConv2DOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const DepthwiseConv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDepthwiseConv2DOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDepthwiseConv2DOptions(flatbuffers::FlatBufferBuilder &_fbb, + const DepthwiseConv2DOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DepthwiseConv2DOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _padding = _o->padding; + auto _stride_w = _o->stride_w; + auto _stride_h = _o->stride_h; + auto _depth_multiplier = _o->depth_multiplier; + auto _fused_activation_function = _o->fused_activation_function; + auto _dilation_w_factor = _o->dilation_w_factor; + auto _dilation_h_factor = _o->dilation_h_factor; + return circle::CreateDepthwiseConv2DOptions(_fbb, _padding, _stride_w, _stride_h, + _depth_multiplier, _fused_activation_function, + _dilation_w_factor, _dilation_h_factor); +} + +inline ConcatEmbeddingsOptionsT * +ConcatEmbeddingsOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ConcatEmbeddingsOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +ConcatEmbeddingsOptions::UnPackTo(ConcatEmbeddingsOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = num_channels(); + _o->num_channels = _e; + } + { + auto _e = num_columns_per_channel(); + if (_e) + { + _o->num_columns_per_channel.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->num_columns_per_channel[_i] = _e->Get(_i); + } + } + } + { + auto _e = embedding_dim_per_channel(); + if (_e) + { + _o->embedding_dim_per_channel.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->embedding_dim_per_channel[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +ConcatEmbeddingsOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const ConcatEmbeddingsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateConcatEmbeddingsOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateConcatEmbeddingsOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ConcatEmbeddingsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ConcatEmbeddingsOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _num_channels = _o->num_channels; + auto _num_columns_per_channel = + _o->num_columns_per_channel.size() ? _fbb.CreateVector(_o->num_columns_per_channel) : 0; + auto _embedding_dim_per_channel = + _o->embedding_dim_per_channel.size() ? _fbb.CreateVector(_o->embedding_dim_per_channel) : 0; + return circle::CreateConcatEmbeddingsOptions(_fbb, _num_channels, _num_columns_per_channel, + _embedding_dim_per_channel); +} + +inline LSHProjectionOptionsT * +LSHProjectionOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LSHProjectionOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LSHProjectionOptions::UnPackTo(LSHProjectionOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = type(); + _o->type = _e; + } +} + +inline flatbuffers::Offset +LSHProjectionOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LSHProjectionOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLSHProjectionOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLSHProjectionOptions(flatbuffers::FlatBufferBuilder &_fbb, const LSHProjectionOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LSHProjectionOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _type = _o->type; + return circle::CreateLSHProjectionOptions(_fbb, _type); +} + +inline SVDFOptionsT *SVDFOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SVDFOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SVDFOptions::UnPackTo(SVDFOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = rank(); + _o->rank = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +SVDFOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SVDFOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSVDFOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSVDFOptions(flatbuffers::FlatBufferBuilder &_fbb, const SVDFOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SVDFOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _rank = _o->rank; + auto _fused_activation_function = _o->fused_activation_function; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateSVDFOptions(_fbb, _rank, _fused_activation_function, + _asymmetric_quantize_inputs); +} + +inline RNNOptionsT *RNNOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new RNNOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void RNNOptions::UnPackTo(RNNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +RNNOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateRNNOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, const RNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const RNNOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateRNNOptions(_fbb, _fused_activation_function, _asymmetric_quantize_inputs); +} + +inline SequenceRNNOptionsT * +SequenceRNNOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SequenceRNNOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SequenceRNNOptions::UnPackTo(SequenceRNNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = time_major(); + _o->time_major = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +SequenceRNNOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSequenceRNNOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSequenceRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, const SequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SequenceRNNOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _time_major = _o->time_major; + auto _fused_activation_function = _o->fused_activation_function; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateSequenceRNNOptions(_fbb, _time_major, _fused_activation_function, + _asymmetric_quantize_inputs); +} + +inline BidirectionalSequenceRNNOptionsT * +BidirectionalSequenceRNNOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = + std::unique_ptr(new BidirectionalSequenceRNNOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +BidirectionalSequenceRNNOptions::UnPackTo(BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = time_major(); + _o->time_major = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = merge_outputs(); + _o->merge_outputs = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +BidirectionalSequenceRNNOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBidirectionalSequenceRNNOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBidirectionalSequenceRNNOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceRNNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BidirectionalSequenceRNNOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _time_major = _o->time_major; + auto _fused_activation_function = _o->fused_activation_function; + auto _merge_outputs = _o->merge_outputs; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateBidirectionalSequenceRNNOptions( + _fbb, _time_major, _fused_activation_function, _merge_outputs, _asymmetric_quantize_inputs); +} + +inline FullyConnectedOptionsT * +FullyConnectedOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new FullyConnectedOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FullyConnectedOptions::UnPackTo(FullyConnectedOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = weights_format(); + _o->weights_format = _e; + } + { + auto _e = keep_num_dims(); + _o->keep_num_dims = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +FullyConnectedOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateFullyConnectedOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateFullyConnectedOptions(flatbuffers::FlatBufferBuilder &_fbb, const FullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const FullyConnectedOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _weights_format = _o->weights_format; + auto _keep_num_dims = _o->keep_num_dims; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateFullyConnectedOptions(_fbb, _fused_activation_function, _weights_format, + _keep_num_dims, _asymmetric_quantize_inputs); +} + +inline SoftmaxOptionsT * +SoftmaxOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SoftmaxOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SoftmaxOptions::UnPackTo(SoftmaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = beta(); + _o->beta = _e; + } +} + +inline flatbuffers::Offset +SoftmaxOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSoftmaxOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const SoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SoftmaxOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _beta = _o->beta; + return circle::CreateSoftmaxOptions(_fbb, _beta); +} + +inline ConcatenationOptionsT * +ConcatenationOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ConcatenationOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ConcatenationOptions::UnPackTo(ConcatenationOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = axis(); + _o->axis = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +ConcatenationOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ConcatenationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateConcatenationOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateConcatenationOptions(flatbuffers::FlatBufferBuilder &_fbb, const ConcatenationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ConcatenationOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _axis = _o->axis; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateConcatenationOptions(_fbb, _axis, _fused_activation_function); +} + +inline AddOptionsT *AddOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new AddOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void AddOptions::UnPackTo(AddOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = pot_scale_int16(); + _o->pot_scale_int16 = _e; + } +} + +inline flatbuffers::Offset +AddOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AddOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateAddOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateAddOptions(flatbuffers::FlatBufferBuilder &_fbb, const AddOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const AddOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _pot_scale_int16 = _o->pot_scale_int16; + return circle::CreateAddOptions(_fbb, _fused_activation_function, _pot_scale_int16); +} + +inline MulOptionsT *MulOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MulOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void MulOptions::UnPackTo(MulOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +MulOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMulOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMulOptions(flatbuffers::FlatBufferBuilder &_fbb, const MulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MulOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateMulOptions(_fbb, _fused_activation_function); +} + +inline L2NormOptionsT * +L2NormOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new L2NormOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void L2NormOptions::UnPackTo(L2NormOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +L2NormOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const L2NormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateL2NormOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateL2NormOptions(flatbuffers::FlatBufferBuilder &_fbb, const L2NormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const L2NormOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateL2NormOptions(_fbb, _fused_activation_function); +} + +inline LocalResponseNormalizationOptionsT * +LocalResponseNormalizationOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = + std::unique_ptr(new LocalResponseNormalizationOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +LocalResponseNormalizationOptions::UnPackTo(LocalResponseNormalizationOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = radius(); + _o->radius = _e; + } + { + auto _e = bias(); + _o->bias = _e; + } + { + auto _e = alpha(); + _o->alpha = _e; + } + { + auto _e = beta(); + _o->beta = _e; + } +} + +inline flatbuffers::Offset +LocalResponseNormalizationOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const LocalResponseNormalizationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLocalResponseNormalizationOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLocalResponseNormalizationOptions(flatbuffers::FlatBufferBuilder &_fbb, + const LocalResponseNormalizationOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LocalResponseNormalizationOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _radius = _o->radius; + auto _bias = _o->bias; + auto _alpha = _o->alpha; + auto _beta = _o->beta; + return circle::CreateLocalResponseNormalizationOptions(_fbb, _radius, _bias, _alpha, _beta); +} + +inline LSTMOptionsT *LSTMOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LSTMOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LSTMOptions::UnPackTo(LSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = cell_clip(); + _o->cell_clip = _e; + } + { + auto _e = proj_clip(); + _o->proj_clip = _e; + } + { + auto _e = kernel_type(); + _o->kernel_type = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +LSTMOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLSTMOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLSTMOptions(flatbuffers::FlatBufferBuilder &_fbb, const LSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LSTMOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _cell_clip = _o->cell_clip; + auto _proj_clip = _o->proj_clip; + auto _kernel_type = _o->kernel_type; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateLSTMOptions(_fbb, _fused_activation_function, _cell_clip, _proj_clip, + _kernel_type, _asymmetric_quantize_inputs); +} + +inline UnidirectionalSequenceLSTMOptionsT * +UnidirectionalSequenceLSTMOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = + std::unique_ptr(new UnidirectionalSequenceLSTMOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +UnidirectionalSequenceLSTMOptions::UnPackTo(UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = cell_clip(); + _o->cell_clip = _e; + } + { + auto _e = proj_clip(); + _o->proj_clip = _e; + } + { + auto _e = time_major(); + _o->time_major = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +UnidirectionalSequenceLSTMOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateUnidirectionalSequenceLSTMOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateUnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &_fbb, + const UnidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const UnidirectionalSequenceLSTMOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _cell_clip = _o->cell_clip; + auto _proj_clip = _o->proj_clip; + auto _time_major = _o->time_major; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateUnidirectionalSequenceLSTMOptions(_fbb, _fused_activation_function, + _cell_clip, _proj_clip, _time_major, + _asymmetric_quantize_inputs); +} + +inline BidirectionalSequenceLSTMOptionsT * +BidirectionalSequenceLSTMOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = + std::unique_ptr(new BidirectionalSequenceLSTMOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +BidirectionalSequenceLSTMOptions::UnPackTo(BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = cell_clip(); + _o->cell_clip = _e; + } + { + auto _e = proj_clip(); + _o->proj_clip = _e; + } + { + auto _e = merge_outputs(); + _o->merge_outputs = _e; + } + { + auto _e = time_major(); + _o->time_major = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +BidirectionalSequenceLSTMOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBidirectionalSequenceLSTMOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BidirectionalSequenceLSTMOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BidirectionalSequenceLSTMOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _cell_clip = _o->cell_clip; + auto _proj_clip = _o->proj_clip; + auto _merge_outputs = _o->merge_outputs; + auto _time_major = _o->time_major; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateBidirectionalSequenceLSTMOptions(_fbb, _fused_activation_function, + _cell_clip, _proj_clip, _merge_outputs, + _time_major, _asymmetric_quantize_inputs); +} + +inline ResizeBilinearOptionsT * +ResizeBilinearOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ResizeBilinearOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ResizeBilinearOptions::UnPackTo(ResizeBilinearOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = align_corners(); + _o->align_corners = _e; + } + { + auto _e = half_pixel_centers(); + _o->half_pixel_centers = _e; + } +} + +inline flatbuffers::Offset +ResizeBilinearOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ResizeBilinearOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateResizeBilinearOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateResizeBilinearOptions(flatbuffers::FlatBufferBuilder &_fbb, const ResizeBilinearOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ResizeBilinearOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _align_corners = _o->align_corners; + auto _half_pixel_centers = _o->half_pixel_centers; + return circle::CreateResizeBilinearOptions(_fbb, _align_corners, _half_pixel_centers); +} + +inline ResizeNearestNeighborOptionsT * +ResizeNearestNeighborOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ResizeNearestNeighborOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +ResizeNearestNeighborOptions::UnPackTo(ResizeNearestNeighborOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = align_corners(); + _o->align_corners = _e; + } + { + auto _e = half_pixel_centers(); + _o->half_pixel_centers = _e; + } +} + +inline flatbuffers::Offset +ResizeNearestNeighborOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const ResizeNearestNeighborOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateResizeNearestNeighborOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateResizeNearestNeighborOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ResizeNearestNeighborOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ResizeNearestNeighborOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _align_corners = _o->align_corners; + auto _half_pixel_centers = _o->half_pixel_centers; + return circle::CreateResizeNearestNeighborOptions(_fbb, _align_corners, _half_pixel_centers); +} + +inline CallOptionsT *CallOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CallOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CallOptions::UnPackTo(CallOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = subgraph(); + _o->subgraph = _e; + } +} + +inline flatbuffers::Offset +CallOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CallOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCallOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCallOptions(flatbuffers::FlatBufferBuilder &_fbb, const CallOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CallOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _subgraph = _o->subgraph; + return circle::CreateCallOptions(_fbb, _subgraph); +} + +inline PadOptionsT *PadOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new PadOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PadOptions::UnPackTo(PadOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +PadOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreatePadOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreatePadOptions(flatbuffers::FlatBufferBuilder &_fbb, const PadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const PadOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreatePadOptions(_fbb); +} + +inline PadV2OptionsT *PadV2Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new PadV2OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PadV2Options::UnPackTo(PadV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +PadV2Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PadV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreatePadV2Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreatePadV2Options(flatbuffers::FlatBufferBuilder &_fbb, const PadV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const PadV2OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreatePadV2Options(_fbb); +} + +inline ReshapeOptionsT * +ReshapeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ReshapeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ReshapeOptions::UnPackTo(ReshapeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = new_shape(); + if (_e) + { + _o->new_shape.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->new_shape[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +ReshapeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReshapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateReshapeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateReshapeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReshapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ReshapeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _new_shape = _o->new_shape.size() ? _fbb.CreateVector(_o->new_shape) : 0; + return circle::CreateReshapeOptions(_fbb, _new_shape); +} + +inline SpaceToBatchNDOptionsT * +SpaceToBatchNDOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SpaceToBatchNDOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SpaceToBatchNDOptions::UnPackTo(SpaceToBatchNDOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SpaceToBatchNDOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToBatchNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSpaceToBatchNDOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSpaceToBatchNDOptions(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToBatchNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SpaceToBatchNDOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSpaceToBatchNDOptions(_fbb); +} + +inline BatchToSpaceNDOptionsT * +BatchToSpaceNDOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BatchToSpaceNDOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void BatchToSpaceNDOptions::UnPackTo(BatchToSpaceNDOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +BatchToSpaceNDOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BatchToSpaceNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBatchToSpaceNDOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBatchToSpaceNDOptions(flatbuffers::FlatBufferBuilder &_fbb, const BatchToSpaceNDOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BatchToSpaceNDOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateBatchToSpaceNDOptions(_fbb); +} + +inline SkipGramOptionsT * +SkipGramOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SkipGramOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SkipGramOptions::UnPackTo(SkipGramOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = ngram_size(); + _o->ngram_size = _e; + } + { + auto _e = max_skip_size(); + _o->max_skip_size = _e; + } + { + auto _e = include_all_ngrams(); + _o->include_all_ngrams = _e; + } +} + +inline flatbuffers::Offset +SkipGramOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SkipGramOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSkipGramOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSkipGramOptions(flatbuffers::FlatBufferBuilder &_fbb, const SkipGramOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SkipGramOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _ngram_size = _o->ngram_size; + auto _max_skip_size = _o->max_skip_size; + auto _include_all_ngrams = _o->include_all_ngrams; + return circle::CreateSkipGramOptions(_fbb, _ngram_size, _max_skip_size, _include_all_ngrams); +} + +inline SpaceToDepthOptionsT * +SpaceToDepthOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SpaceToDepthOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SpaceToDepthOptions::UnPackTo(SpaceToDepthOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = block_size(); + _o->block_size = _e; + } +} + +inline flatbuffers::Offset +SpaceToDepthOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToDepthOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSpaceToDepthOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSpaceToDepthOptions(flatbuffers::FlatBufferBuilder &_fbb, const SpaceToDepthOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SpaceToDepthOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _block_size = _o->block_size; + return circle::CreateSpaceToDepthOptions(_fbb, _block_size); +} + +inline DepthToSpaceOptionsT * +DepthToSpaceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DepthToSpaceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void DepthToSpaceOptions::UnPackTo(DepthToSpaceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = block_size(); + _o->block_size = _e; + } +} + +inline flatbuffers::Offset +DepthToSpaceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const DepthToSpaceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDepthToSpaceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDepthToSpaceOptions(flatbuffers::FlatBufferBuilder &_fbb, const DepthToSpaceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DepthToSpaceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _block_size = _o->block_size; + return circle::CreateDepthToSpaceOptions(_fbb, _block_size); +} + +inline SubOptionsT *SubOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SubOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SubOptions::UnPackTo(SubOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } + { + auto _e = pot_scale_int16(); + _o->pot_scale_int16 = _e; + } +} + +inline flatbuffers::Offset +SubOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SubOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSubOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSubOptions(flatbuffers::FlatBufferBuilder &_fbb, const SubOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SubOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + auto _pot_scale_int16 = _o->pot_scale_int16; + return circle::CreateSubOptions(_fbb, _fused_activation_function, _pot_scale_int16); +} + +inline DivOptionsT *DivOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DivOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void DivOptions::UnPackTo(DivOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +DivOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const DivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDivOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDivOptions(flatbuffers::FlatBufferBuilder &_fbb, const DivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DivOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateDivOptions(_fbb, _fused_activation_function); +} + +inline TopKV2OptionsT * +TopKV2Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TopKV2OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TopKV2Options::UnPackTo(TopKV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +TopKV2Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TopKV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTopKV2Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateTopKV2Options(flatbuffers::FlatBufferBuilder &_fbb, const TopKV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TopKV2OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateTopKV2Options(_fbb); +} + +inline EmbeddingLookupSparseOptionsT * +EmbeddingLookupSparseOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new EmbeddingLookupSparseOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +EmbeddingLookupSparseOptions::UnPackTo(EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = combiner(); + _o->combiner = _e; + } +} + +inline flatbuffers::Offset +EmbeddingLookupSparseOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateEmbeddingLookupSparseOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateEmbeddingLookupSparseOptions(flatbuffers::FlatBufferBuilder &_fbb, + const EmbeddingLookupSparseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const EmbeddingLookupSparseOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _combiner = _o->combiner; + return circle::CreateEmbeddingLookupSparseOptions(_fbb, _combiner); +} + +inline GatherOptionsT * +GatherOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new GatherOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void GatherOptions::UnPackTo(GatherOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = axis(); + _o->axis = _e; + } + { + auto _e = batch_dims(); + _o->batch_dims = _e; + } +} + +inline flatbuffers::Offset +GatherOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateGatherOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, const GatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const GatherOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _axis = _o->axis; + auto _batch_dims = _o->batch_dims; + return circle::CreateGatherOptions(_fbb, _axis, _batch_dims); +} + +inline TransposeOptionsT * +TransposeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TransposeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TransposeOptions::UnPackTo(TransposeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +TransposeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TransposeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTransposeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateTransposeOptions(flatbuffers::FlatBufferBuilder &_fbb, const TransposeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TransposeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateTransposeOptions(_fbb); +} + +inline ExpOptionsT *ExpOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ExpOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ExpOptions::UnPackTo(ExpOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ExpOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ExpOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateExpOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateExpOptions(flatbuffers::FlatBufferBuilder &_fbb, const ExpOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ExpOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateExpOptions(_fbb); +} + +inline CosOptionsT *CosOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CosOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CosOptions::UnPackTo(CosOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +CosOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CosOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCosOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCosOptions(flatbuffers::FlatBufferBuilder &_fbb, const CosOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CosOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateCosOptions(_fbb); +} + +inline ReducerOptionsT * +ReducerOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ReducerOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ReducerOptions::UnPackTo(ReducerOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = keep_dims(); + _o->keep_dims = _e; + } +} + +inline flatbuffers::Offset +ReducerOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReducerOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateReducerOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateReducerOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReducerOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ReducerOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _keep_dims = _o->keep_dims; + return circle::CreateReducerOptions(_fbb, _keep_dims); +} + +inline SqueezeOptionsT * +SqueezeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SqueezeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SqueezeOptions::UnPackTo(SqueezeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = squeeze_dims(); + if (_e) + { + _o->squeeze_dims.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->squeeze_dims[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +SqueezeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SqueezeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSqueezeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSqueezeOptions(flatbuffers::FlatBufferBuilder &_fbb, const SqueezeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SqueezeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _squeeze_dims = _o->squeeze_dims.size() ? _fbb.CreateVector(_o->squeeze_dims) : 0; + return circle::CreateSqueezeOptions(_fbb, _squeeze_dims); +} + +inline SplitOptionsT *SplitOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SplitOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SplitOptions::UnPackTo(SplitOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = num_splits(); + _o->num_splits = _e; + } +} + +inline flatbuffers::Offset +SplitOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSplitOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSplitOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SplitOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _num_splits = _o->num_splits; + return circle::CreateSplitOptions(_fbb, _num_splits); +} + +inline SplitVOptionsT * +SplitVOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SplitVOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SplitVOptions::UnPackTo(SplitVOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = num_splits(); + _o->num_splits = _e; + } +} + +inline flatbuffers::Offset +SplitVOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSplitVOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSplitVOptions(flatbuffers::FlatBufferBuilder &_fbb, const SplitVOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SplitVOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _num_splits = _o->num_splits; + return circle::CreateSplitVOptions(_fbb, _num_splits); +} + +inline StridedSliceOptionsT * +StridedSliceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new StridedSliceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void StridedSliceOptions::UnPackTo(StridedSliceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = begin_mask(); + _o->begin_mask = _e; + } + { + auto _e = end_mask(); + _o->end_mask = _e; + } + { + auto _e = ellipsis_mask(); + _o->ellipsis_mask = _e; + } + { + auto _e = new_axis_mask(); + _o->new_axis_mask = _e; + } + { + auto _e = shrink_axis_mask(); + _o->shrink_axis_mask = _e; + } +} + +inline flatbuffers::Offset +StridedSliceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const StridedSliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateStridedSliceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateStridedSliceOptions(flatbuffers::FlatBufferBuilder &_fbb, const StridedSliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const StridedSliceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _begin_mask = _o->begin_mask; + auto _end_mask = _o->end_mask; + auto _ellipsis_mask = _o->ellipsis_mask; + auto _new_axis_mask = _o->new_axis_mask; + auto _shrink_axis_mask = _o->shrink_axis_mask; + return circle::CreateStridedSliceOptions(_fbb, _begin_mask, _end_mask, _ellipsis_mask, + _new_axis_mask, _shrink_axis_mask); +} + +inline LogSoftmaxOptionsT * +LogSoftmaxOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LogSoftmaxOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LogSoftmaxOptions::UnPackTo(LogSoftmaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LogSoftmaxOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogSoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLogSoftmaxOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLogSoftmaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogSoftmaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LogSoftmaxOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLogSoftmaxOptions(_fbb); +} + +inline CastOptionsT *CastOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CastOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CastOptions::UnPackTo(CastOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = in_data_type(); + _o->in_data_type = _e; + } + { + auto _e = out_data_type(); + _o->out_data_type = _e; + } +} + +inline flatbuffers::Offset +CastOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CastOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCastOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCastOptions(flatbuffers::FlatBufferBuilder &_fbb, const CastOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CastOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _in_data_type = _o->in_data_type; + auto _out_data_type = _o->out_data_type; + return circle::CreateCastOptions(_fbb, _in_data_type, _out_data_type); +} + +inline DequantizeOptionsT * +DequantizeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DequantizeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void DequantizeOptions::UnPackTo(DequantizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +DequantizeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const DequantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDequantizeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDequantizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const DequantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DequantizeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateDequantizeOptions(_fbb); +} + +inline MaximumMinimumOptionsT * +MaximumMinimumOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MaximumMinimumOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void MaximumMinimumOptions::UnPackTo(MaximumMinimumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +MaximumMinimumOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MaximumMinimumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMaximumMinimumOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMaximumMinimumOptions(flatbuffers::FlatBufferBuilder &_fbb, const MaximumMinimumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MaximumMinimumOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateMaximumMinimumOptions(_fbb); +} + +inline TileOptionsT *TileOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TileOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TileOptions::UnPackTo(TileOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +TileOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTileOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateTileOptions(flatbuffers::FlatBufferBuilder &_fbb, const TileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TileOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateTileOptions(_fbb); +} + +inline ArgMaxOptionsT * +ArgMaxOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ArgMaxOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ArgMaxOptions::UnPackTo(ArgMaxOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = output_type(); + _o->output_type = _e; + } +} + +inline flatbuffers::Offset +ArgMaxOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArgMaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateArgMaxOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateArgMaxOptions(flatbuffers::FlatBufferBuilder &_fbb, const ArgMaxOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ArgMaxOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _output_type = _o->output_type; + return circle::CreateArgMaxOptions(_fbb, _output_type); +} + +inline ArgMinOptionsT * +ArgMinOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ArgMinOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ArgMinOptions::UnPackTo(ArgMinOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = output_type(); + _o->output_type = _e; + } +} + +inline flatbuffers::Offset +ArgMinOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ArgMinOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateArgMinOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateArgMinOptions(flatbuffers::FlatBufferBuilder &_fbb, const ArgMinOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ArgMinOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _output_type = _o->output_type; + return circle::CreateArgMinOptions(_fbb, _output_type); +} + +inline GreaterOptionsT * +GreaterOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new GreaterOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void GreaterOptions::UnPackTo(GreaterOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +GreaterOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GreaterOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateGreaterOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateGreaterOptions(flatbuffers::FlatBufferBuilder &_fbb, const GreaterOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const GreaterOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateGreaterOptions(_fbb); +} + +inline GreaterEqualOptionsT * +GreaterEqualOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new GreaterEqualOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void GreaterEqualOptions::UnPackTo(GreaterEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +GreaterEqualOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GreaterEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateGreaterEqualOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateGreaterEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const GreaterEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const GreaterEqualOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateGreaterEqualOptions(_fbb); +} + +inline LessOptionsT *LessOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LessOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LessOptions::UnPackTo(LessOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LessOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LessOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLessOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLessOptions(flatbuffers::FlatBufferBuilder &_fbb, const LessOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LessOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLessOptions(_fbb); +} + +inline LessEqualOptionsT * +LessEqualOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LessEqualOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LessEqualOptions::UnPackTo(LessEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LessEqualOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LessEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLessEqualOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLessEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const LessEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LessEqualOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLessEqualOptions(_fbb); +} + +inline NegOptionsT *NegOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new NegOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void NegOptions::UnPackTo(NegOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +NegOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const NegOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateNegOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateNegOptions(flatbuffers::FlatBufferBuilder &_fbb, const NegOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const NegOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateNegOptions(_fbb); +} + +inline SelectOptionsT * +SelectOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SelectOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SelectOptions::UnPackTo(SelectOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SelectOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SelectOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSelectOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSelectOptions(flatbuffers::FlatBufferBuilder &_fbb, const SelectOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SelectOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSelectOptions(_fbb); +} + +inline SliceOptionsT *SliceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SliceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SliceOptions::UnPackTo(SliceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SliceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSliceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSliceOptions(flatbuffers::FlatBufferBuilder &_fbb, const SliceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SliceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSliceOptions(_fbb); +} + +inline TransposeConvOptionsT * +TransposeConvOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TransposeConvOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TransposeConvOptions::UnPackTo(TransposeConvOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = padding(); + _o->padding = _e; + } + { + auto _e = stride_w(); + _o->stride_w = _e; + } + { + auto _e = stride_h(); + _o->stride_h = _e; + } +} + +inline flatbuffers::Offset +TransposeConvOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TransposeConvOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTransposeConvOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateTransposeConvOptions(flatbuffers::FlatBufferBuilder &_fbb, const TransposeConvOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TransposeConvOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _padding = _o->padding; + auto _stride_w = _o->stride_w; + auto _stride_h = _o->stride_h; + return circle::CreateTransposeConvOptions(_fbb, _padding, _stride_w, _stride_h); +} + +inline ExpandDimsOptionsT * +ExpandDimsOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ExpandDimsOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ExpandDimsOptions::UnPackTo(ExpandDimsOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ExpandDimsOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ExpandDimsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateExpandDimsOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateExpandDimsOptions(flatbuffers::FlatBufferBuilder &_fbb, const ExpandDimsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ExpandDimsOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateExpandDimsOptions(_fbb); +} + +inline SparseToDenseOptionsT * +SparseToDenseOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SparseToDenseOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SparseToDenseOptions::UnPackTo(SparseToDenseOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = validate_indices(); + _o->validate_indices = _e; + } +} + +inline flatbuffers::Offset +SparseToDenseOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SparseToDenseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSparseToDenseOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSparseToDenseOptions(flatbuffers::FlatBufferBuilder &_fbb, const SparseToDenseOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SparseToDenseOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _validate_indices = _o->validate_indices; + return circle::CreateSparseToDenseOptions(_fbb, _validate_indices); +} + +inline EqualOptionsT *EqualOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new EqualOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void EqualOptions::UnPackTo(EqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +EqualOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const EqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateEqualOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const EqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const EqualOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateEqualOptions(_fbb); +} + +inline NotEqualOptionsT * +NotEqualOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new NotEqualOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void NotEqualOptions::UnPackTo(NotEqualOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +NotEqualOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const NotEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateNotEqualOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateNotEqualOptions(flatbuffers::FlatBufferBuilder &_fbb, const NotEqualOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const NotEqualOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateNotEqualOptions(_fbb); +} + +inline ShapeOptionsT *ShapeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ShapeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ShapeOptions::UnPackTo(ShapeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = out_type(); + _o->out_type = _e; + } +} + +inline flatbuffers::Offset +ShapeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ShapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateShapeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateShapeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ShapeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ShapeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _out_type = _o->out_type; + return circle::CreateShapeOptions(_fbb, _out_type); +} + +inline RankOptionsT *RankOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new RankOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void RankOptions::UnPackTo(RankOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +RankOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RankOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateRankOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateRankOptions(flatbuffers::FlatBufferBuilder &_fbb, const RankOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const RankOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateRankOptions(_fbb); +} + +inline PowOptionsT *PowOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new PowOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PowOptions::UnPackTo(PowOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +PowOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PowOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreatePowOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreatePowOptions(flatbuffers::FlatBufferBuilder &_fbb, const PowOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const PowOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreatePowOptions(_fbb); +} + +inline FakeQuantOptionsT * +FakeQuantOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new FakeQuantOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FakeQuantOptions::UnPackTo(FakeQuantOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = min(); + _o->min = _e; + } + { + auto _e = max(); + _o->max = _e; + } + { + auto _e = num_bits(); + _o->num_bits = _e; + } + { + auto _e = narrow_range(); + _o->narrow_range = _e; + } +} + +inline flatbuffers::Offset +FakeQuantOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FakeQuantOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateFakeQuantOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateFakeQuantOptions(flatbuffers::FlatBufferBuilder &_fbb, const FakeQuantOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const FakeQuantOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _min = _o->min; + auto _max = _o->max; + auto _num_bits = _o->num_bits; + auto _narrow_range = _o->narrow_range; + return circle::CreateFakeQuantOptions(_fbb, _min, _max, _num_bits, _narrow_range); +} + +inline PackOptionsT *PackOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new PackOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void PackOptions::UnPackTo(PackOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = values_count(); + _o->values_count = _e; + } + { + auto _e = axis(); + _o->axis = _e; + } +} + +inline flatbuffers::Offset +PackOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const PackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreatePackOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreatePackOptions(flatbuffers::FlatBufferBuilder &_fbb, const PackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const PackOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _values_count = _o->values_count; + auto _axis = _o->axis; + return circle::CreatePackOptions(_fbb, _values_count, _axis); +} + +inline LogicalOrOptionsT * +LogicalOrOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LogicalOrOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LogicalOrOptions::UnPackTo(LogicalOrOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LogicalOrOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalOrOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLogicalOrOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLogicalOrOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalOrOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LogicalOrOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLogicalOrOptions(_fbb); +} + +inline OneHotOptionsT * +OneHotOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new OneHotOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void OneHotOptions::UnPackTo(OneHotOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = axis(); + _o->axis = _e; + } +} + +inline flatbuffers::Offset +OneHotOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const OneHotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateOneHotOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateOneHotOptions(flatbuffers::FlatBufferBuilder &_fbb, const OneHotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const OneHotOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _axis = _o->axis; + return circle::CreateOneHotOptions(_fbb, _axis); +} + +inline AbsOptionsT *AbsOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new AbsOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void AbsOptions::UnPackTo(AbsOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +AbsOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateAbsOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateAbsOptions(flatbuffers::FlatBufferBuilder &_fbb, const AbsOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const AbsOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateAbsOptions(_fbb); +} + +inline HardSwishOptionsT * +HardSwishOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new HardSwishOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void HardSwishOptions::UnPackTo(HardSwishOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +HardSwishOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const HardSwishOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateHardSwishOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateHardSwishOptions(flatbuffers::FlatBufferBuilder &_fbb, const HardSwishOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const HardSwishOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateHardSwishOptions(_fbb); +} + +inline LogicalAndOptionsT * +LogicalAndOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LogicalAndOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LogicalAndOptions::UnPackTo(LogicalAndOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LogicalAndOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalAndOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLogicalAndOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLogicalAndOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalAndOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LogicalAndOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLogicalAndOptions(_fbb); +} + +inline LogicalNotOptionsT * +LogicalNotOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LogicalNotOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LogicalNotOptions::UnPackTo(LogicalNotOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +LogicalNotOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LogicalNotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLogicalNotOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLogicalNotOptions(flatbuffers::FlatBufferBuilder &_fbb, const LogicalNotOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LogicalNotOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateLogicalNotOptions(_fbb); +} + +inline UnpackOptionsT * +UnpackOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new UnpackOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void UnpackOptions::UnPackTo(UnpackOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = num(); + _o->num = _e; + } + { + auto _e = axis(); + _o->axis = _e; + } +} + +inline flatbuffers::Offset +UnpackOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnpackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateUnpackOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateUnpackOptions(flatbuffers::FlatBufferBuilder &_fbb, const UnpackOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const UnpackOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _num = _o->num; + auto _axis = _o->axis; + return circle::CreateUnpackOptions(_fbb, _num, _axis); +} + +inline FloorDivOptionsT * +FloorDivOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new FloorDivOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FloorDivOptions::UnPackTo(FloorDivOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +FloorDivOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FloorDivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateFloorDivOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateFloorDivOptions(flatbuffers::FlatBufferBuilder &_fbb, const FloorDivOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const FloorDivOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateFloorDivOptions(_fbb); +} + +inline SquareOptionsT * +SquareOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SquareOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SquareOptions::UnPackTo(SquareOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SquareOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SquareOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSquareOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSquareOptions(flatbuffers::FlatBufferBuilder &_fbb, const SquareOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SquareOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSquareOptions(_fbb); +} + +inline ZerosLikeOptionsT * +ZerosLikeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ZerosLikeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ZerosLikeOptions::UnPackTo(ZerosLikeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ZerosLikeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ZerosLikeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateZerosLikeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateZerosLikeOptions(flatbuffers::FlatBufferBuilder &_fbb, const ZerosLikeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ZerosLikeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateZerosLikeOptions(_fbb); +} + +inline FillOptionsT *FillOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new FillOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FillOptions::UnPackTo(FillOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +FillOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FillOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateFillOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateFillOptions(flatbuffers::FlatBufferBuilder &_fbb, const FillOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const FillOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateFillOptions(_fbb); +} + +inline FloorModOptionsT * +FloorModOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new FloorModOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void FloorModOptions::UnPackTo(FloorModOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +FloorModOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const FloorModOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateFloorModOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateFloorModOptions(flatbuffers::FlatBufferBuilder &_fbb, const FloorModOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const FloorModOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateFloorModOptions(_fbb); +} + +inline RangeOptionsT *RangeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new RangeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void RangeOptions::UnPackTo(RangeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +RangeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RangeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateRangeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateRangeOptions(flatbuffers::FlatBufferBuilder &_fbb, const RangeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const RangeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateRangeOptions(_fbb); +} + +inline LeakyReluOptionsT * +LeakyReluOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new LeakyReluOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void LeakyReluOptions::UnPackTo(LeakyReluOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = alpha(); + _o->alpha = _e; + } +} + +inline flatbuffers::Offset +LeakyReluOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateLeakyReluOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateLeakyReluOptions(flatbuffers::FlatBufferBuilder &_fbb, const LeakyReluOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const LeakyReluOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _alpha = _o->alpha; + return circle::CreateLeakyReluOptions(_fbb, _alpha); +} + +inline SquaredDifferenceOptionsT * +SquaredDifferenceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SquaredDifferenceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +SquaredDifferenceOptions::UnPackTo(SquaredDifferenceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SquaredDifferenceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const SquaredDifferenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSquaredDifferenceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &_fbb, + const SquaredDifferenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SquaredDifferenceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSquaredDifferenceOptions(_fbb); +} + +inline MirrorPadOptionsT * +MirrorPadOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MirrorPadOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void MirrorPadOptions::UnPackTo(MirrorPadOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = mode(); + _o->mode = _e; + } +} + +inline flatbuffers::Offset +MirrorPadOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMirrorPadOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMirrorPadOptions(flatbuffers::FlatBufferBuilder &_fbb, const MirrorPadOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MirrorPadOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _mode = _o->mode; + return circle::CreateMirrorPadOptions(_fbb, _mode); +} + +inline UniqueOptionsT * +UniqueOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new UniqueOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void UniqueOptions::UnPackTo(UniqueOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = idx_out_type(); + _o->idx_out_type = _e; + } +} + +inline flatbuffers::Offset +UniqueOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateUniqueOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateUniqueOptions(flatbuffers::FlatBufferBuilder &_fbb, const UniqueOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const UniqueOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _idx_out_type = _o->idx_out_type; + return circle::CreateUniqueOptions(_fbb, _idx_out_type); +} + +inline ReverseV2OptionsT * +ReverseV2Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ReverseV2OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ReverseV2Options::UnPackTo(ReverseV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ReverseV2Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReverseV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateReverseV2Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateReverseV2Options(flatbuffers::FlatBufferBuilder &_fbb, const ReverseV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ReverseV2OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateReverseV2Options(_fbb); +} + +inline AddNOptionsT *AddNOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new AddNOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void AddNOptions::UnPackTo(AddNOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +AddNOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AddNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateAddNOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateAddNOptions(flatbuffers::FlatBufferBuilder &_fbb, const AddNOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const AddNOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateAddNOptions(_fbb); +} + +inline GatherNdOptionsT * +GatherNdOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new GatherNdOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void GatherNdOptions::UnPackTo(GatherNdOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +GatherNdOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const GatherNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateGatherNdOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateGatherNdOptions(flatbuffers::FlatBufferBuilder &_fbb, const GatherNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const GatherNdOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateGatherNdOptions(_fbb); +} + +inline WhereOptionsT *WhereOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new WhereOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void WhereOptions::UnPackTo(WhereOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +WhereOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const WhereOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateWhereOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateWhereOptions(flatbuffers::FlatBufferBuilder &_fbb, const WhereOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const WhereOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateWhereOptions(_fbb); +} + +inline ReverseSequenceOptionsT * +ReverseSequenceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ReverseSequenceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +ReverseSequenceOptions::UnPackTo(ReverseSequenceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = seq_dim(); + _o->seq_dim = _e; + } + { + auto _e = batch_dim(); + _o->batch_dim = _e; + } +} + +inline flatbuffers::Offset +ReverseSequenceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const ReverseSequenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateReverseSequenceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateReverseSequenceOptions(flatbuffers::FlatBufferBuilder &_fbb, + const ReverseSequenceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ReverseSequenceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _seq_dim = _o->seq_dim; + auto _batch_dim = _o->batch_dim; + return circle::CreateReverseSequenceOptions(_fbb, _seq_dim, _batch_dim); +} + +inline MatrixDiagOptionsT * +MatrixDiagOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MatrixDiagOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void MatrixDiagOptions::UnPackTo(MatrixDiagOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +MatrixDiagOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MatrixDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMatrixDiagOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMatrixDiagOptions(flatbuffers::FlatBufferBuilder &_fbb, const MatrixDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MatrixDiagOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateMatrixDiagOptions(_fbb); +} + +inline QuantizeOptionsT * +QuantizeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new QuantizeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void QuantizeOptions::UnPackTo(QuantizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +QuantizeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const QuantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateQuantizeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateQuantizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const QuantizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const QuantizeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateQuantizeOptions(_fbb); +} + +inline MatrixSetDiagOptionsT * +MatrixSetDiagOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MatrixSetDiagOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void MatrixSetDiagOptions::UnPackTo(MatrixSetDiagOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +MatrixSetDiagOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MatrixSetDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMatrixSetDiagOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMatrixSetDiagOptions(flatbuffers::FlatBufferBuilder &_fbb, const MatrixSetDiagOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MatrixSetDiagOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateMatrixSetDiagOptions(_fbb); +} + +inline IfOptionsT *IfOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new IfOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void IfOptions::UnPackTo(IfOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = then_subgraph_index(); + _o->then_subgraph_index = _e; + } + { + auto _e = else_subgraph_index(); + _o->else_subgraph_index = _e; + } +} + +inline flatbuffers::Offset +IfOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const IfOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateIfOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateIfOptions(flatbuffers::FlatBufferBuilder &_fbb, const IfOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const IfOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _then_subgraph_index = _o->then_subgraph_index; + auto _else_subgraph_index = _o->else_subgraph_index; + return circle::CreateIfOptions(_fbb, _then_subgraph_index, _else_subgraph_index); +} + +inline CallOnceOptionsT * +CallOnceOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CallOnceOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CallOnceOptions::UnPackTo(CallOnceOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = init_subgraph_index(); + _o->init_subgraph_index = _e; + } +} + +inline flatbuffers::Offset +CallOnceOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CallOnceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCallOnceOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCallOnceOptions(flatbuffers::FlatBufferBuilder &_fbb, const CallOnceOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CallOnceOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _init_subgraph_index = _o->init_subgraph_index; + return circle::CreateCallOnceOptions(_fbb, _init_subgraph_index); +} + +inline WhileOptionsT *WhileOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new WhileOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void WhileOptions::UnPackTo(WhileOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = cond_subgraph_index(); + _o->cond_subgraph_index = _e; + } + { + auto _e = body_subgraph_index(); + _o->body_subgraph_index = _e; + } +} + +inline flatbuffers::Offset +WhileOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const WhileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateWhileOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateWhileOptions(flatbuffers::FlatBufferBuilder &_fbb, const WhileOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const WhileOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _cond_subgraph_index = _o->cond_subgraph_index; + auto _body_subgraph_index = _o->body_subgraph_index; + return circle::CreateWhileOptions(_fbb, _cond_subgraph_index, _body_subgraph_index); +} + +inline NonMaxSuppressionV4OptionsT * +NonMaxSuppressionV4Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new NonMaxSuppressionV4OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +NonMaxSuppressionV4Options::UnPackTo(NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +NonMaxSuppressionV4Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateNonMaxSuppressionV4Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateNonMaxSuppressionV4Options(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV4OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const NonMaxSuppressionV4OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateNonMaxSuppressionV4Options(_fbb); +} + +inline NonMaxSuppressionV5OptionsT * +NonMaxSuppressionV5Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new NonMaxSuppressionV5OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +NonMaxSuppressionV5Options::UnPackTo(NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +NonMaxSuppressionV5Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateNonMaxSuppressionV5Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateNonMaxSuppressionV5Options(flatbuffers::FlatBufferBuilder &_fbb, + const NonMaxSuppressionV5OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const NonMaxSuppressionV5OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateNonMaxSuppressionV5Options(_fbb); +} + +inline ScatterNdOptionsT * +ScatterNdOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ScatterNdOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ScatterNdOptions::UnPackTo(ScatterNdOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ScatterNdOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ScatterNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateScatterNdOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateScatterNdOptions(flatbuffers::FlatBufferBuilder &_fbb, const ScatterNdOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ScatterNdOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateScatterNdOptions(_fbb); +} + +inline SelectV2OptionsT * +SelectV2Options::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SelectV2OptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SelectV2Options::UnPackTo(SelectV2OptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SelectV2Options::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SelectV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSelectV2Options(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSelectV2Options(flatbuffers::FlatBufferBuilder &_fbb, const SelectV2OptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SelectV2OptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSelectV2Options(_fbb); +} + +inline DensifyOptionsT * +DensifyOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new DensifyOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void DensifyOptions::UnPackTo(DensifyOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +DensifyOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const DensifyOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateDensifyOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateDensifyOptions(flatbuffers::FlatBufferBuilder &_fbb, const DensifyOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const DensifyOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateDensifyOptions(_fbb); +} + +inline SegmentSumOptionsT * +SegmentSumOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SegmentSumOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SegmentSumOptions::UnPackTo(SegmentSumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +SegmentSumOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SegmentSumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSegmentSumOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSegmentSumOptions(flatbuffers::FlatBufferBuilder &_fbb, const SegmentSumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SegmentSumOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateSegmentSumOptions(_fbb); +} + +inline BatchMatMulOptionsT * +BatchMatMulOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BatchMatMulOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void BatchMatMulOptions::UnPackTo(BatchMatMulOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = adjoint_lhs(); + _o->adjoint_lhs = _e; + } + { + auto _e = adjoint_rhs(); + _o->adjoint_rhs = _e; + } + { + auto _e = asymmetric_quantize_inputs(); + _o->asymmetric_quantize_inputs = _e; + } +} + +inline flatbuffers::Offset +BatchMatMulOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BatchMatMulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBatchMatMulOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBatchMatMulOptions(flatbuffers::FlatBufferBuilder &_fbb, const BatchMatMulOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BatchMatMulOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _adjoint_lhs = _o->adjoint_lhs; + auto _adjoint_rhs = _o->adjoint_rhs; + auto _asymmetric_quantize_inputs = _o->asymmetric_quantize_inputs; + return circle::CreateBatchMatMulOptions(_fbb, _adjoint_lhs, _adjoint_rhs, + _asymmetric_quantize_inputs); +} + +inline CumsumOptionsT * +CumsumOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new CumsumOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void CumsumOptions::UnPackTo(CumsumOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = exclusive(); + _o->exclusive = _e; + } + { + auto _e = reverse(); + _o->reverse = _e; + } +} + +inline flatbuffers::Offset +CumsumOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CumsumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateCumsumOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateCumsumOptions(flatbuffers::FlatBufferBuilder &_fbb, const CumsumOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const CumsumOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _exclusive = _o->exclusive; + auto _reverse = _o->reverse; + return circle::CreateCumsumOptions(_fbb, _exclusive, _reverse); +} + +inline BroadcastToOptionsT * +BroadcastToOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BroadcastToOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void BroadcastToOptions::UnPackTo(BroadcastToOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +BroadcastToOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BroadcastToOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBroadcastToOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBroadcastToOptions(flatbuffers::FlatBufferBuilder &_fbb, const BroadcastToOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BroadcastToOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateBroadcastToOptions(_fbb); +} + +inline Rfft2dOptionsT * +Rfft2dOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new Rfft2dOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Rfft2dOptions::UnPackTo(Rfft2dOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +Rfft2dOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const Rfft2dOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateRfft2dOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateRfft2dOptions(flatbuffers::FlatBufferBuilder &_fbb, const Rfft2dOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const Rfft2dOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateRfft2dOptions(_fbb); +} + +inline HashtableOptionsT * +HashtableOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new HashtableOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void HashtableOptions::UnPackTo(HashtableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = table_id(); + _o->table_id = _e; + } + { + auto _e = key_dtype(); + _o->key_dtype = _e; + } + { + auto _e = value_dtype(); + _o->value_dtype = _e; + } +} + +inline flatbuffers::Offset +HashtableOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateHashtableOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateHashtableOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const HashtableOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _table_id = _o->table_id; + auto _key_dtype = _o->key_dtype; + auto _value_dtype = _o->value_dtype; + return circle::CreateHashtableOptions(_fbb, _table_id, _key_dtype, _value_dtype); +} + +inline HashtableFindOptionsT * +HashtableFindOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new HashtableFindOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void HashtableFindOptions::UnPackTo(HashtableFindOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +HashtableFindOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableFindOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateHashtableFindOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateHashtableFindOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableFindOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const HashtableFindOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateHashtableFindOptions(_fbb); +} + +inline HashtableImportOptionsT * +HashtableImportOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new HashtableImportOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +HashtableImportOptions::UnPackTo(HashtableImportOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +HashtableImportOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const HashtableImportOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateHashtableImportOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateHashtableImportOptions(flatbuffers::FlatBufferBuilder &_fbb, + const HashtableImportOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const HashtableImportOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateHashtableImportOptions(_fbb); +} + +inline HashtableSizeOptionsT * +HashtableSizeOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new HashtableSizeOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void HashtableSizeOptions::UnPackTo(HashtableSizeOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +HashtableSizeOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const HashtableSizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateHashtableSizeOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateHashtableSizeOptions(flatbuffers::FlatBufferBuilder &_fbb, const HashtableSizeOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const HashtableSizeOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateHashtableSizeOptions(_fbb); +} + +inline VarHandleOptionsT * +VarHandleOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new VarHandleOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void VarHandleOptions::UnPackTo(VarHandleOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = container(); + if (_e) + _o->container = _e->str(); + } + { + auto _e = shared_name(); + if (_e) + _o->shared_name = _e->str(); + } +} + +inline flatbuffers::Offset +VarHandleOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const VarHandleOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateVarHandleOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateVarHandleOptions(flatbuffers::FlatBufferBuilder &_fbb, const VarHandleOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const VarHandleOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _container = _o->container.empty() ? 0 : _fbb.CreateString(_o->container); + auto _shared_name = _o->shared_name.empty() ? 0 : _fbb.CreateString(_o->shared_name); + return circle::CreateVarHandleOptions(_fbb, _container, _shared_name); +} + +inline ReadVariableOptionsT * +ReadVariableOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ReadVariableOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void ReadVariableOptions::UnPackTo(ReadVariableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +ReadVariableOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const ReadVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateReadVariableOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateReadVariableOptions(flatbuffers::FlatBufferBuilder &_fbb, const ReadVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ReadVariableOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateReadVariableOptions(_fbb); +} + +inline AssignVariableOptionsT * +AssignVariableOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new AssignVariableOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void AssignVariableOptions::UnPackTo(AssignVariableOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; +} + +inline flatbuffers::Offset +AssignVariableOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AssignVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateAssignVariableOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateAssignVariableOptions(flatbuffers::FlatBufferBuilder &_fbb, const AssignVariableOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const AssignVariableOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + return circle::CreateAssignVariableOptions(_fbb); +} + +inline RandomOptionsT * +RandomOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new RandomOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void RandomOptions::UnPackTo(RandomOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = seed(); + _o->seed = _e; + } + { + auto _e = seed2(); + _o->seed2 = _e; + } +} + +inline flatbuffers::Offset +RandomOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateRandomOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateRandomOptions(flatbuffers::FlatBufferBuilder &_fbb, const RandomOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const RandomOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _seed = _o->seed; + auto _seed2 = _o->seed2; + return circle::CreateRandomOptions(_fbb, _seed, _seed2); +} + +inline BCQGatherOptionsT * +BCQGatherOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BCQGatherOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void BCQGatherOptions::UnPackTo(BCQGatherOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = input_hidden_size(); + _o->input_hidden_size = _e; + } + { + auto _e = axis(); + _o->axis = _e; + } +} + +inline flatbuffers::Offset +BCQGatherOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const BCQGatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBCQGatherOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBCQGatherOptions(flatbuffers::FlatBufferBuilder &_fbb, const BCQGatherOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BCQGatherOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _input_hidden_size = _o->input_hidden_size; + auto _axis = _o->axis; + return circle::CreateBCQGatherOptions(_fbb, _input_hidden_size, _axis); +} + +inline BCQFullyConnectedOptionsT * +BCQFullyConnectedOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BCQFullyConnectedOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void +BCQFullyConnectedOptions::UnPackTo(BCQFullyConnectedOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = weights_hidden_size(); + _o->weights_hidden_size = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +BCQFullyConnectedOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const BCQFullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBCQFullyConnectedOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateBCQFullyConnectedOptions(flatbuffers::FlatBufferBuilder &_fbb, + const BCQFullyConnectedOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BCQFullyConnectedOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _weights_hidden_size = _o->weights_hidden_size; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateBCQFullyConnectedOptions(_fbb, _weights_hidden_size, + _fused_activation_function); +} + +inline InstanceNormOptionsT * +InstanceNormOptions::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new InstanceNormOptionsT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void InstanceNormOptions::UnPackTo(InstanceNormOptionsT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = epsilon(); + _o->epsilon = _e; + } + { + auto _e = fused_activation_function(); + _o->fused_activation_function = _e; + } +} + +inline flatbuffers::Offset +InstanceNormOptions::Pack(flatbuffers::FlatBufferBuilder &_fbb, const InstanceNormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateInstanceNormOptions(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateInstanceNormOptions(flatbuffers::FlatBufferBuilder &_fbb, const InstanceNormOptionsT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const InstanceNormOptionsT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _epsilon = _o->epsilon; + auto _fused_activation_function = _o->fused_activation_function; + return circle::CreateInstanceNormOptions(_fbb, _epsilon, _fused_activation_function); +} + +inline OperatorCodeT *OperatorCode::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new OperatorCodeT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void OperatorCode::UnPackTo(OperatorCodeT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = deprecated_builtin_code(); + _o->deprecated_builtin_code = _e; + } + { + auto _e = custom_code(); + if (_e) + _o->custom_code = _e->str(); + } + { + auto _e = version(); + _o->version = _e; + } + { + auto _e = builtin_code(); + _o->builtin_code = _e; + } +} + +inline flatbuffers::Offset +OperatorCode::Pack(flatbuffers::FlatBufferBuilder &_fbb, const OperatorCodeT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateOperatorCode(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateOperatorCode(flatbuffers::FlatBufferBuilder &_fbb, const OperatorCodeT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const OperatorCodeT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _deprecated_builtin_code = _o->deprecated_builtin_code; + auto _custom_code = _o->custom_code.empty() ? 0 : _fbb.CreateString(_o->custom_code); + auto _version = _o->version; + auto _builtin_code = _o->builtin_code; + return circle::CreateOperatorCode(_fbb, _deprecated_builtin_code, _custom_code, _version, + _builtin_code); +} + +inline OperatorT *Operator::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new OperatorT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Operator::UnPackTo(OperatorT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = opcode_index(); + _o->opcode_index = _e; + } + { + auto _e = inputs(); + if (_e) + { + _o->inputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->inputs[_i] = _e->Get(_i); + } + } + } + { + auto _e = outputs(); + if (_e) + { + _o->outputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->outputs[_i] = _e->Get(_i); + } + } + } + { + auto _e = builtin_options_type(); + _o->builtin_options.type = _e; + } + { + auto _e = builtin_options(); + if (_e) + _o->builtin_options.value = + circle::BuiltinOptionsUnion::UnPack(_e, builtin_options_type(), _resolver); + } + { + auto _e = custom_options(); + if (_e) + { + _o->custom_options.resize(_e->size()); + std::copy(_e->begin(), _e->end(), _o->custom_options.begin()); + } + } + { + auto _e = custom_options_format(); + _o->custom_options_format = _e; + } + { + auto _e = mutating_variable_inputs(); + if (_e) + { + _o->mutating_variable_inputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->mutating_variable_inputs[_i] = _e->Get(_i) != 0; + } + } + } + { + auto _e = intermediates(); + if (_e) + { + _o->intermediates.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->intermediates[_i] = _e->Get(_i); + } + } + } +} + +inline flatbuffers::Offset +Operator::Pack(flatbuffers::FlatBufferBuilder &_fbb, const OperatorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateOperator(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateOperator(flatbuffers::FlatBufferBuilder &_fbb, const OperatorT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const OperatorT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _opcode_index = _o->opcode_index; + auto _inputs = _o->inputs.size() ? _fbb.CreateVector(_o->inputs) : 0; + auto _outputs = _o->outputs.size() ? _fbb.CreateVector(_o->outputs) : 0; + auto _builtin_options_type = _o->builtin_options.type; + auto _builtin_options = _o->builtin_options.Pack(_fbb); + auto _custom_options = _o->custom_options.size() ? _fbb.CreateVector(_o->custom_options) : 0; + auto _custom_options_format = _o->custom_options_format; + auto _mutating_variable_inputs = + _o->mutating_variable_inputs.size() ? _fbb.CreateVector(_o->mutating_variable_inputs) : 0; + auto _intermediates = _o->intermediates.size() ? _fbb.CreateVector(_o->intermediates) : 0; + return circle::CreateOperator(_fbb, _opcode_index, _inputs, _outputs, _builtin_options_type, + _builtin_options, _custom_options, _custom_options_format, + _mutating_variable_inputs, _intermediates); +} + +inline SubGraphT *SubGraph::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SubGraphT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SubGraph::UnPackTo(SubGraphT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = tensors(); + if (_e) + { + _o->tensors.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->tensors[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = inputs(); + if (_e) + { + _o->inputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->inputs[_i] = _e->Get(_i); + } + } + } + { + auto _e = outputs(); + if (_e) + { + _o->outputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->outputs[_i] = _e->Get(_i); + } + } + } + { + auto _e = operators(); + if (_e) + { + _o->operators.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->operators[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = name(); + if (_e) + _o->name = _e->str(); + } + { + auto _e = data_format(); + _o->data_format = _e; + } +} + +inline flatbuffers::Offset +SubGraph::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SubGraphT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSubGraph(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSubGraph(flatbuffers::FlatBufferBuilder &_fbb, const SubGraphT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SubGraphT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _tensors = + _o->tensors.size() + ? _fbb.CreateVector>( + _o->tensors.size(), + [](size_t i, _VectorArgs *__va) { + return CreateTensor(*__va->__fbb, __va->__o->tensors[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _inputs = _o->inputs.size() ? _fbb.CreateVector(_o->inputs) : 0; + auto _outputs = _o->outputs.size() ? _fbb.CreateVector(_o->outputs) : 0; + auto _operators = + _o->operators.size() + ? _fbb.CreateVector>( + _o->operators.size(), + [](size_t i, _VectorArgs *__va) { + return CreateOperator(*__va->__fbb, __va->__o->operators[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); + auto _data_format = _o->data_format; + return circle::CreateSubGraph(_fbb, _tensors, _inputs, _outputs, _operators, _name, _data_format); +} + +inline BufferT *Buffer::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new BufferT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Buffer::UnPackTo(BufferT *_o, const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = data(); + if (_e) + { + _o->data.resize(_e->size()); + std::copy(_e->begin(), _e->end(), _o->data.begin()); + } + } +} + +inline flatbuffers::Offset Buffer::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const BufferT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateBuffer(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateBuffer(flatbuffers::FlatBufferBuilder &_fbb, + const BufferT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const BufferT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + _fbb.ForceVectorAlignment(_o->data.size(), sizeof(uint8_t), 16); + auto _data = _o->data.size() ? _fbb.CreateVector(_o->data) : 0; + return circle::CreateBuffer(_fbb, _data); +} + +inline MetadataT *Metadata::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new MetadataT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Metadata::UnPackTo(MetadataT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = name(); + if (_e) + _o->name = _e->str(); + } + { + auto _e = buffer(); + _o->buffer = _e; + } +} + +inline flatbuffers::Offset +Metadata::Pack(flatbuffers::FlatBufferBuilder &_fbb, const MetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateMetadata(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateMetadata(flatbuffers::FlatBufferBuilder &_fbb, const MetadataT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const MetadataT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); + auto _buffer = _o->buffer; + return circle::CreateMetadata(_fbb, _name, _buffer); +} + +inline TensorMapT *TensorMap::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new TensorMapT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TensorMap::UnPackTo(TensorMapT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = name(); + if (_e) + _o->name = _e->str(); + } + { + auto _e = tensor_index(); + _o->tensor_index = _e; + } +} + +inline flatbuffers::Offset +TensorMap::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TensorMapT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateTensorMap(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateTensorMap(flatbuffers::FlatBufferBuilder &_fbb, const TensorMapT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const TensorMapT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); + auto _tensor_index = _o->tensor_index; + return circle::CreateTensorMap(_fbb, _name, _tensor_index); +} + +inline SignatureDefT *SignatureDef::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new SignatureDefT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void SignatureDef::UnPackTo(SignatureDefT *_o, + const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = inputs(); + if (_e) + { + _o->inputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->inputs[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = outputs(); + if (_e) + { + _o->outputs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->outputs[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = signature_key(); + if (_e) + _o->signature_key = _e->str(); + } + { + auto _e = subgraph_index(); + _o->subgraph_index = _e; + } +} + +inline flatbuffers::Offset +SignatureDef::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SignatureDefT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateSignatureDef(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset +CreateSignatureDef(flatbuffers::FlatBufferBuilder &_fbb, const SignatureDefT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const SignatureDefT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _inputs = + _o->inputs.size() + ? _fbb.CreateVector>( + _o->inputs.size(), + [](size_t i, _VectorArgs *__va) { + return CreateTensorMap(*__va->__fbb, __va->__o->inputs[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _outputs = + _o->outputs.size() + ? _fbb.CreateVector>( + _o->outputs.size(), + [](size_t i, _VectorArgs *__va) { + return CreateTensorMap(*__va->__fbb, __va->__o->outputs[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _signature_key = _o->signature_key.empty() ? 0 : _fbb.CreateString(_o->signature_key); + auto _subgraph_index = _o->subgraph_index; + return circle::CreateSignatureDef(_fbb, _inputs, _outputs, _signature_key, _subgraph_index); +} + +inline ModelT *Model::UnPack(const flatbuffers::resolver_function_t *_resolver) const +{ + auto _o = std::unique_ptr(new ModelT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void Model::UnPackTo(ModelT *_o, const flatbuffers::resolver_function_t *_resolver) const +{ + (void)_o; + (void)_resolver; + { + auto _e = version(); + _o->version = _e; + } + { + auto _e = operator_codes(); + if (_e) + { + _o->operator_codes.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->operator_codes[_i] = + std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = subgraphs(); + if (_e) + { + _o->subgraphs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->subgraphs[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = description(); + if (_e) + _o->description = _e->str(); + } + { + auto _e = buffers(); + if (_e) + { + _o->buffers.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->buffers[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = metadata_buffer(); + if (_e) + { + _o->metadata_buffer.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->metadata_buffer[_i] = _e->Get(_i); + } + } + } + { + auto _e = metadata(); + if (_e) + { + _o->metadata.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->metadata[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } + { + auto _e = signature_defs(); + if (_e) + { + _o->signature_defs.resize(_e->size()); + for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) + { + _o->signature_defs[_i] = + std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); + } + } + } +} + +inline flatbuffers::Offset Model::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const ModelT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + return CreateModel(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateModel(flatbuffers::FlatBufferBuilder &_fbb, + const ModelT *_o, + const flatbuffers::rehasher_function_t *_rehasher) +{ + (void)_rehasher; + (void)_o; + struct _VectorArgs + { + flatbuffers::FlatBufferBuilder *__fbb; + const ModelT *__o; + const flatbuffers::rehasher_function_t *__rehasher; + } _va = {&_fbb, _o, _rehasher}; + (void)_va; + auto _version = _o->version; + auto _operator_codes = + _o->operator_codes.size() + ? _fbb.CreateVector>( + _o->operator_codes.size(), + [](size_t i, _VectorArgs *__va) { + return CreateOperatorCode(*__va->__fbb, __va->__o->operator_codes[i].get(), + __va->__rehasher); + }, + &_va) + : 0; + auto _subgraphs = + _o->subgraphs.size() + ? _fbb.CreateVector>( + _o->subgraphs.size(), + [](size_t i, _VectorArgs *__va) { + return CreateSubGraph(*__va->__fbb, __va->__o->subgraphs[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _description = _o->description.empty() ? 0 : _fbb.CreateString(_o->description); + auto _buffers = + _o->buffers.size() + ? _fbb.CreateVector>( + _o->buffers.size(), + [](size_t i, _VectorArgs *__va) { + return CreateBuffer(*__va->__fbb, __va->__o->buffers[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _metadata_buffer = _o->metadata_buffer.size() ? _fbb.CreateVector(_o->metadata_buffer) : 0; + auto _metadata = + _o->metadata.size() + ? _fbb.CreateVector>( + _o->metadata.size(), + [](size_t i, _VectorArgs *__va) { + return CreateMetadata(*__va->__fbb, __va->__o->metadata[i].get(), __va->__rehasher); + }, + &_va) + : 0; + auto _signature_defs = + _o->signature_defs.size() + ? _fbb.CreateVector>( + _o->signature_defs.size(), + [](size_t i, _VectorArgs *__va) { + return CreateSignatureDef(*__va->__fbb, __va->__o->signature_defs[i].get(), + __va->__rehasher); + }, + &_va) + : 0; + return circle::CreateModel(_fbb, _version, _operator_codes, _subgraphs, _description, _buffers, + _metadata_buffer, _metadata, _signature_defs); +} + +inline bool VerifyQuantizationDetails(flatbuffers::Verifier &verifier, const void *obj, + QuantizationDetails type) +{ + switch (type) + { + case QuantizationDetails_NONE: + { + return true; + } + case QuantizationDetails_CustomQuantization: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: + return true; + } +} + +inline bool +VerifyQuantizationDetailsVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types) +{ + if (!values || !types) + return !values && !types; + if (values->size() != types->size()) + return false; + for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) + { + if (!VerifyQuantizationDetails(verifier, values->Get(i), + types->GetEnum(i))) + { + return false; + } + } + return true; +} + +inline void *QuantizationDetailsUnion::UnPack(const void *obj, QuantizationDetails type, + const flatbuffers::resolver_function_t *resolver) +{ + switch (type) + { + case QuantizationDetails_CustomQuantization: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + default: + return nullptr; + } +} + +inline flatbuffers::Offset +QuantizationDetailsUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher) const +{ + switch (type) + { + case QuantizationDetails_CustomQuantization: + { + auto ptr = reinterpret_cast(value); + return CreateCustomQuantization(_fbb, ptr, _rehasher).Union(); + } + default: + return 0; + } +} + +inline QuantizationDetailsUnion::QuantizationDetailsUnion(const QuantizationDetailsUnion &u) + : type(u.type), value(nullptr) +{ + switch (type) + { + case QuantizationDetails_CustomQuantization: + { + value = + new circle::CustomQuantizationT(*reinterpret_cast(u.value)); + break; + } + default: + break; + } +} + +inline void QuantizationDetailsUnion::Reset() +{ + switch (type) + { + case QuantizationDetails_CustomQuantization: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + default: + break; + } + value = nullptr; + type = QuantizationDetails_NONE; +} + +inline bool VerifySparseIndexVector(flatbuffers::Verifier &verifier, const void *obj, + SparseIndexVector type) +{ + switch (type) + { + case SparseIndexVector_NONE: + { + return true; + } + case SparseIndexVector_Int32Vector: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case SparseIndexVector_Uint16Vector: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case SparseIndexVector_Uint8Vector: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: + return true; + } +} + +inline bool +VerifySparseIndexVectorVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types) +{ + if (!values || !types) + return !values && !types; + if (values->size() != types->size()) + return false; + for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) + { + if (!VerifySparseIndexVector(verifier, values->Get(i), types->GetEnum(i))) + { + return false; + } + } + return true; +} + +inline void *SparseIndexVectorUnion::UnPack(const void *obj, SparseIndexVector type, + const flatbuffers::resolver_function_t *resolver) +{ + switch (type) + { + case SparseIndexVector_Int32Vector: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case SparseIndexVector_Uint16Vector: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case SparseIndexVector_Uint8Vector: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + default: + return nullptr; + } +} + +inline flatbuffers::Offset +SparseIndexVectorUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher) const +{ + switch (type) + { + case SparseIndexVector_Int32Vector: + { + auto ptr = reinterpret_cast(value); + return CreateInt32Vector(_fbb, ptr, _rehasher).Union(); + } + case SparseIndexVector_Uint16Vector: + { + auto ptr = reinterpret_cast(value); + return CreateUint16Vector(_fbb, ptr, _rehasher).Union(); + } + case SparseIndexVector_Uint8Vector: + { + auto ptr = reinterpret_cast(value); + return CreateUint8Vector(_fbb, ptr, _rehasher).Union(); + } + default: + return 0; + } +} + +inline SparseIndexVectorUnion::SparseIndexVectorUnion(const SparseIndexVectorUnion &u) + : type(u.type), value(nullptr) +{ + switch (type) + { + case SparseIndexVector_Int32Vector: + { + value = new circle::Int32VectorT(*reinterpret_cast(u.value)); + break; + } + case SparseIndexVector_Uint16Vector: + { + value = new circle::Uint16VectorT(*reinterpret_cast(u.value)); + break; + } + case SparseIndexVector_Uint8Vector: + { + value = new circle::Uint8VectorT(*reinterpret_cast(u.value)); + break; + } + default: + break; + } +} + +inline void SparseIndexVectorUnion::Reset() +{ + switch (type) + { + case SparseIndexVector_Int32Vector: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case SparseIndexVector_Uint16Vector: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case SparseIndexVector_Uint8Vector: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + default: + break; + } + value = nullptr; + type = SparseIndexVector_NONE; +} + +inline bool VerifyBuiltinOptions(flatbuffers::Verifier &verifier, const void *obj, + BuiltinOptions type) +{ + switch (type) + { + case BuiltinOptions_NONE: + { + return true; + } + case BuiltinOptions_Conv2DOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_DepthwiseConv2DOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ConcatEmbeddingsOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LSHProjectionOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_Pool2DOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SVDFOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_RNNOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_FullyConnectedOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SoftmaxOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ConcatenationOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_AddOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_L2NormOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LocalResponseNormalizationOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ResizeBilinearOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_CallOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ReshapeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SkipGramOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SpaceToDepthOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_EmbeddingLookupSparseOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MulOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_PadOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_GatherOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BatchToSpaceNDOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SpaceToBatchNDOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_TransposeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ReducerOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SubOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_DivOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SqueezeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SequenceRNNOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_StridedSliceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ExpOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_TopKV2Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SplitOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LogSoftmaxOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_CastOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_DequantizeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MaximumMinimumOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ArgMaxOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LessOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_NegOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_PadV2Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_GreaterOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_GreaterEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LessEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SelectOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SliceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_TransposeConvOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SparseToDenseOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_TileOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ExpandDimsOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_EqualOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_NotEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ShapeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_PowOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ArgMinOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_FakeQuantOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_PackOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LogicalOrOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_OneHotOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LogicalAndOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LogicalNotOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_UnpackOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_FloorDivOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SquareOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ZerosLikeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_FillOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BidirectionalSequenceRNNOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_UnidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_FloorModOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_RangeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ResizeNearestNeighborOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_LeakyReluOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SquaredDifferenceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MirrorPadOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_AbsOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SplitVOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_UniqueOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ReverseV2Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_AddNOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_GatherNdOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_CosOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_WhereOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_RankOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ReverseSequenceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MatrixDiagOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_QuantizeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_MatrixSetDiagOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_HardSwishOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_IfOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_WhileOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_DepthToSpaceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_NonMaxSuppressionV4Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_NonMaxSuppressionV5Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ScatterNdOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SelectV2Options: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_DensifyOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_SegmentSumOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BatchMatMulOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_CumsumOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_CallOnceOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BroadcastToOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_Rfft2dOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_Conv3DOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_HashtableOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_HashtableFindOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_HashtableImportOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_HashtableSizeOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_VarHandleOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_ReadVariableOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_AssignVariableOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_RandomOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BCQGatherOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_BCQFullyConnectedOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case BuiltinOptions_InstanceNormOptions: + { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: + return true; + } +} + +inline bool VerifyBuiltinOptionsVector(flatbuffers::Verifier &verifier, + const flatbuffers::Vector> *values, + const flatbuffers::Vector *types) +{ + if (!values || !types) + return !values && !types; + if (values->size() != types->size()) + return false; + for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) + { + if (!VerifyBuiltinOptions(verifier, values->Get(i), types->GetEnum(i))) + { + return false; + } + } + return true; +} + +inline void *BuiltinOptionsUnion::UnPack(const void *obj, BuiltinOptions type, + const flatbuffers::resolver_function_t *resolver) +{ + switch (type) + { + case BuiltinOptions_Conv2DOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_DepthwiseConv2DOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ConcatEmbeddingsOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LSHProjectionOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_Pool2DOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SVDFOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_RNNOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_FullyConnectedOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SoftmaxOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ConcatenationOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_AddOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_L2NormOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LocalResponseNormalizationOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ResizeBilinearOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_CallOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ReshapeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SkipGramOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SpaceToDepthOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_EmbeddingLookupSparseOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MulOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_PadOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_GatherOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BatchToSpaceNDOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SpaceToBatchNDOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_TransposeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ReducerOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SubOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_DivOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SqueezeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SequenceRNNOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_StridedSliceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ExpOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_TopKV2Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SplitOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LogSoftmaxOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_CastOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_DequantizeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MaximumMinimumOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ArgMaxOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LessOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_NegOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_PadV2Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_GreaterOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_GreaterEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LessEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SelectOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SliceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_TransposeConvOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SparseToDenseOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_TileOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ExpandDimsOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_EqualOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_NotEqualOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ShapeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_PowOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ArgMinOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_FakeQuantOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_PackOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LogicalOrOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_OneHotOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LogicalAndOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LogicalNotOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_UnpackOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_FloorDivOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SquareOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ZerosLikeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_FillOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BidirectionalSequenceRNNOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_UnidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_FloorModOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_RangeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ResizeNearestNeighborOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_LeakyReluOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SquaredDifferenceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MirrorPadOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_AbsOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SplitVOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_UniqueOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ReverseV2Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_AddNOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_GatherNdOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_CosOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_WhereOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_RankOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ReverseSequenceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MatrixDiagOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_QuantizeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_MatrixSetDiagOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_HardSwishOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_IfOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_WhileOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_DepthToSpaceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_NonMaxSuppressionV4Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_NonMaxSuppressionV5Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ScatterNdOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SelectV2Options: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_DensifyOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_SegmentSumOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BatchMatMulOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_CumsumOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_CallOnceOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BroadcastToOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_Rfft2dOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_Conv3DOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_HashtableOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_HashtableFindOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_HashtableImportOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_HashtableSizeOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_VarHandleOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_ReadVariableOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_AssignVariableOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_RandomOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BCQGatherOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_BCQFullyConnectedOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case BuiltinOptions_InstanceNormOptions: + { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + default: + return nullptr; + } +} + +inline flatbuffers::Offset +BuiltinOptionsUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb, + const flatbuffers::rehasher_function_t *_rehasher) const +{ + switch (type) + { + case BuiltinOptions_Conv2DOptions: + { + auto ptr = reinterpret_cast(value); + return CreateConv2DOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_DepthwiseConv2DOptions: + { + auto ptr = reinterpret_cast(value); + return CreateDepthwiseConv2DOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ConcatEmbeddingsOptions: + { + auto ptr = reinterpret_cast(value); + return CreateConcatEmbeddingsOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LSHProjectionOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLSHProjectionOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_Pool2DOptions: + { + auto ptr = reinterpret_cast(value); + return CreatePool2DOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SVDFOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSVDFOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_RNNOptions: + { + auto ptr = reinterpret_cast(value); + return CreateRNNOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_FullyConnectedOptions: + { + auto ptr = reinterpret_cast(value); + return CreateFullyConnectedOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SoftmaxOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSoftmaxOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ConcatenationOptions: + { + auto ptr = reinterpret_cast(value); + return CreateConcatenationOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_AddOptions: + { + auto ptr = reinterpret_cast(value); + return CreateAddOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_L2NormOptions: + { + auto ptr = reinterpret_cast(value); + return CreateL2NormOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LocalResponseNormalizationOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLocalResponseNormalizationOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LSTMOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLSTMOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ResizeBilinearOptions: + { + auto ptr = reinterpret_cast(value); + return CreateResizeBilinearOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_CallOptions: + { + auto ptr = reinterpret_cast(value); + return CreateCallOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ReshapeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateReshapeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SkipGramOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSkipGramOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SpaceToDepthOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSpaceToDepthOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_EmbeddingLookupSparseOptions: + { + auto ptr = reinterpret_cast(value); + return CreateEmbeddingLookupSparseOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MulOptions: + { + auto ptr = reinterpret_cast(value); + return CreateMulOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_PadOptions: + { + auto ptr = reinterpret_cast(value); + return CreatePadOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_GatherOptions: + { + auto ptr = reinterpret_cast(value); + return CreateGatherOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BatchToSpaceNDOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBatchToSpaceNDOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SpaceToBatchNDOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSpaceToBatchNDOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_TransposeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateTransposeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ReducerOptions: + { + auto ptr = reinterpret_cast(value); + return CreateReducerOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SubOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSubOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_DivOptions: + { + auto ptr = reinterpret_cast(value); + return CreateDivOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SqueezeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSqueezeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SequenceRNNOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSequenceRNNOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_StridedSliceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateStridedSliceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ExpOptions: + { + auto ptr = reinterpret_cast(value); + return CreateExpOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_TopKV2Options: + { + auto ptr = reinterpret_cast(value); + return CreateTopKV2Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SplitOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSplitOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LogSoftmaxOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLogSoftmaxOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_CastOptions: + { + auto ptr = reinterpret_cast(value); + return CreateCastOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_DequantizeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateDequantizeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MaximumMinimumOptions: + { + auto ptr = reinterpret_cast(value); + return CreateMaximumMinimumOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ArgMaxOptions: + { + auto ptr = reinterpret_cast(value); + return CreateArgMaxOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LessOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLessOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_NegOptions: + { + auto ptr = reinterpret_cast(value); + return CreateNegOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_PadV2Options: + { + auto ptr = reinterpret_cast(value); + return CreatePadV2Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_GreaterOptions: + { + auto ptr = reinterpret_cast(value); + return CreateGreaterOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_GreaterEqualOptions: + { + auto ptr = reinterpret_cast(value); + return CreateGreaterEqualOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LessEqualOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLessEqualOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SelectOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSelectOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SliceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSliceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_TransposeConvOptions: + { + auto ptr = reinterpret_cast(value); + return CreateTransposeConvOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SparseToDenseOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSparseToDenseOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_TileOptions: + { + auto ptr = reinterpret_cast(value); + return CreateTileOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ExpandDimsOptions: + { + auto ptr = reinterpret_cast(value); + return CreateExpandDimsOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_EqualOptions: + { + auto ptr = reinterpret_cast(value); + return CreateEqualOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_NotEqualOptions: + { + auto ptr = reinterpret_cast(value); + return CreateNotEqualOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ShapeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateShapeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_PowOptions: + { + auto ptr = reinterpret_cast(value); + return CreatePowOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ArgMinOptions: + { + auto ptr = reinterpret_cast(value); + return CreateArgMinOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_FakeQuantOptions: + { + auto ptr = reinterpret_cast(value); + return CreateFakeQuantOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_PackOptions: + { + auto ptr = reinterpret_cast(value); + return CreatePackOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LogicalOrOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLogicalOrOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_OneHotOptions: + { + auto ptr = reinterpret_cast(value); + return CreateOneHotOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LogicalAndOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLogicalAndOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LogicalNotOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLogicalNotOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_UnpackOptions: + { + auto ptr = reinterpret_cast(value); + return CreateUnpackOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_FloorDivOptions: + { + auto ptr = reinterpret_cast(value); + return CreateFloorDivOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SquareOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSquareOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ZerosLikeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateZerosLikeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_FillOptions: + { + auto ptr = reinterpret_cast(value); + return CreateFillOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBidirectionalSequenceLSTMOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BidirectionalSequenceRNNOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBidirectionalSequenceRNNOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_UnidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(value); + return CreateUnidirectionalSequenceLSTMOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_FloorModOptions: + { + auto ptr = reinterpret_cast(value); + return CreateFloorModOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_RangeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateRangeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ResizeNearestNeighborOptions: + { + auto ptr = reinterpret_cast(value); + return CreateResizeNearestNeighborOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_LeakyReluOptions: + { + auto ptr = reinterpret_cast(value); + return CreateLeakyReluOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SquaredDifferenceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSquaredDifferenceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MirrorPadOptions: + { + auto ptr = reinterpret_cast(value); + return CreateMirrorPadOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_AbsOptions: + { + auto ptr = reinterpret_cast(value); + return CreateAbsOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SplitVOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSplitVOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_UniqueOptions: + { + auto ptr = reinterpret_cast(value); + return CreateUniqueOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ReverseV2Options: + { + auto ptr = reinterpret_cast(value); + return CreateReverseV2Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_AddNOptions: + { + auto ptr = reinterpret_cast(value); + return CreateAddNOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_GatherNdOptions: + { + auto ptr = reinterpret_cast(value); + return CreateGatherNdOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_CosOptions: + { + auto ptr = reinterpret_cast(value); + return CreateCosOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_WhereOptions: + { + auto ptr = reinterpret_cast(value); + return CreateWhereOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_RankOptions: + { + auto ptr = reinterpret_cast(value); + return CreateRankOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ReverseSequenceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateReverseSequenceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MatrixDiagOptions: + { + auto ptr = reinterpret_cast(value); + return CreateMatrixDiagOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_QuantizeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateQuantizeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_MatrixSetDiagOptions: + { + auto ptr = reinterpret_cast(value); + return CreateMatrixSetDiagOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_HardSwishOptions: + { + auto ptr = reinterpret_cast(value); + return CreateHardSwishOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_IfOptions: + { + auto ptr = reinterpret_cast(value); + return CreateIfOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_WhileOptions: + { + auto ptr = reinterpret_cast(value); + return CreateWhileOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_DepthToSpaceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateDepthToSpaceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_NonMaxSuppressionV4Options: + { + auto ptr = reinterpret_cast(value); + return CreateNonMaxSuppressionV4Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_NonMaxSuppressionV5Options: + { + auto ptr = reinterpret_cast(value); + return CreateNonMaxSuppressionV5Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ScatterNdOptions: + { + auto ptr = reinterpret_cast(value); + return CreateScatterNdOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SelectV2Options: + { + auto ptr = reinterpret_cast(value); + return CreateSelectV2Options(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_DensifyOptions: + { + auto ptr = reinterpret_cast(value); + return CreateDensifyOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_SegmentSumOptions: + { + auto ptr = reinterpret_cast(value); + return CreateSegmentSumOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BatchMatMulOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBatchMatMulOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_CumsumOptions: + { + auto ptr = reinterpret_cast(value); + return CreateCumsumOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_CallOnceOptions: + { + auto ptr = reinterpret_cast(value); + return CreateCallOnceOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BroadcastToOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBroadcastToOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_Rfft2dOptions: + { + auto ptr = reinterpret_cast(value); + return CreateRfft2dOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_Conv3DOptions: + { + auto ptr = reinterpret_cast(value); + return CreateConv3DOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_HashtableOptions: + { + auto ptr = reinterpret_cast(value); + return CreateHashtableOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_HashtableFindOptions: + { + auto ptr = reinterpret_cast(value); + return CreateHashtableFindOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_HashtableImportOptions: + { + auto ptr = reinterpret_cast(value); + return CreateHashtableImportOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_HashtableSizeOptions: + { + auto ptr = reinterpret_cast(value); + return CreateHashtableSizeOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_VarHandleOptions: + { + auto ptr = reinterpret_cast(value); + return CreateVarHandleOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_ReadVariableOptions: + { + auto ptr = reinterpret_cast(value); + return CreateReadVariableOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_AssignVariableOptions: + { + auto ptr = reinterpret_cast(value); + return CreateAssignVariableOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_RandomOptions: + { + auto ptr = reinterpret_cast(value); + return CreateRandomOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BCQGatherOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBCQGatherOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_BCQFullyConnectedOptions: + { + auto ptr = reinterpret_cast(value); + return CreateBCQFullyConnectedOptions(_fbb, ptr, _rehasher).Union(); + } + case BuiltinOptions_InstanceNormOptions: + { + auto ptr = reinterpret_cast(value); + return CreateInstanceNormOptions(_fbb, ptr, _rehasher).Union(); + } + default: + return 0; + } +} + +inline BuiltinOptionsUnion::BuiltinOptionsUnion(const BuiltinOptionsUnion &u) + : type(u.type), value(nullptr) +{ + switch (type) + { + case BuiltinOptions_Conv2DOptions: + { + value = new circle::Conv2DOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_DepthwiseConv2DOptions: + { + value = new circle::DepthwiseConv2DOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ConcatEmbeddingsOptions: + { + value = new circle::ConcatEmbeddingsOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LSHProjectionOptions: + { + value = new circle::LSHProjectionOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_Pool2DOptions: + { + value = new circle::Pool2DOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SVDFOptions: + { + value = new circle::SVDFOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_RNNOptions: + { + value = new circle::RNNOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_FullyConnectedOptions: + { + value = new circle::FullyConnectedOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SoftmaxOptions: + { + value = new circle::SoftmaxOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ConcatenationOptions: + { + value = new circle::ConcatenationOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_AddOptions: + { + value = new circle::AddOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_L2NormOptions: + { + value = new circle::L2NormOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LocalResponseNormalizationOptions: + { + value = new circle::LocalResponseNormalizationOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LSTMOptions: + { + value = new circle::LSTMOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ResizeBilinearOptions: + { + value = new circle::ResizeBilinearOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_CallOptions: + { + value = new circle::CallOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ReshapeOptions: + { + value = new circle::ReshapeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SkipGramOptions: + { + value = new circle::SkipGramOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SpaceToDepthOptions: + { + value = new circle::SpaceToDepthOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_EmbeddingLookupSparseOptions: + { + value = new circle::EmbeddingLookupSparseOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MulOptions: + { + value = new circle::MulOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_PadOptions: + { + value = new circle::PadOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_GatherOptions: + { + value = new circle::GatherOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BatchToSpaceNDOptions: + { + value = new circle::BatchToSpaceNDOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SpaceToBatchNDOptions: + { + value = new circle::SpaceToBatchNDOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_TransposeOptions: + { + value = + new circle::TransposeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ReducerOptions: + { + value = new circle::ReducerOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SubOptions: + { + value = new circle::SubOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_DivOptions: + { + value = new circle::DivOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SqueezeOptions: + { + value = new circle::SqueezeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SequenceRNNOptions: + { + value = + new circle::SequenceRNNOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_StridedSliceOptions: + { + value = new circle::StridedSliceOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ExpOptions: + { + value = new circle::ExpOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_TopKV2Options: + { + value = new circle::TopKV2OptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SplitOptions: + { + value = new circle::SplitOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LogSoftmaxOptions: + { + value = + new circle::LogSoftmaxOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_CastOptions: + { + value = new circle::CastOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_DequantizeOptions: + { + value = + new circle::DequantizeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MaximumMinimumOptions: + { + value = new circle::MaximumMinimumOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ArgMaxOptions: + { + value = new circle::ArgMaxOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LessOptions: + { + value = new circle::LessOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_NegOptions: + { + value = new circle::NegOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_PadV2Options: + { + value = new circle::PadV2OptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_GreaterOptions: + { + value = new circle::GreaterOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_GreaterEqualOptions: + { + value = new circle::GreaterEqualOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LessEqualOptions: + { + value = + new circle::LessEqualOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SelectOptions: + { + value = new circle::SelectOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SliceOptions: + { + value = new circle::SliceOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_TransposeConvOptions: + { + value = new circle::TransposeConvOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SparseToDenseOptions: + { + value = new circle::SparseToDenseOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_TileOptions: + { + value = new circle::TileOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ExpandDimsOptions: + { + value = + new circle::ExpandDimsOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_EqualOptions: + { + value = new circle::EqualOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_NotEqualOptions: + { + value = new circle::NotEqualOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ShapeOptions: + { + value = new circle::ShapeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_PowOptions: + { + value = new circle::PowOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ArgMinOptions: + { + value = new circle::ArgMinOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_FakeQuantOptions: + { + value = + new circle::FakeQuantOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_PackOptions: + { + value = new circle::PackOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LogicalOrOptions: + { + value = + new circle::LogicalOrOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_OneHotOptions: + { + value = new circle::OneHotOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LogicalAndOptions: + { + value = + new circle::LogicalAndOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LogicalNotOptions: + { + value = + new circle::LogicalNotOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_UnpackOptions: + { + value = new circle::UnpackOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_FloorDivOptions: + { + value = new circle::FloorDivOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SquareOptions: + { + value = new circle::SquareOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ZerosLikeOptions: + { + value = + new circle::ZerosLikeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_FillOptions: + { + value = new circle::FillOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BidirectionalSequenceLSTMOptions: + { + value = new circle::BidirectionalSequenceLSTMOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BidirectionalSequenceRNNOptions: + { + value = new circle::BidirectionalSequenceRNNOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_UnidirectionalSequenceLSTMOptions: + { + value = new circle::UnidirectionalSequenceLSTMOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_FloorModOptions: + { + value = new circle::FloorModOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_RangeOptions: + { + value = new circle::RangeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ResizeNearestNeighborOptions: + { + value = new circle::ResizeNearestNeighborOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_LeakyReluOptions: + { + value = + new circle::LeakyReluOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SquaredDifferenceOptions: + { + value = new circle::SquaredDifferenceOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MirrorPadOptions: + { + value = + new circle::MirrorPadOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_AbsOptions: + { + value = new circle::AbsOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SplitVOptions: + { + value = new circle::SplitVOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_UniqueOptions: + { + value = new circle::UniqueOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ReverseV2Options: + { + value = + new circle::ReverseV2OptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_AddNOptions: + { + value = new circle::AddNOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_GatherNdOptions: + { + value = new circle::GatherNdOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_CosOptions: + { + value = new circle::CosOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_WhereOptions: + { + value = new circle::WhereOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_RankOptions: + { + value = new circle::RankOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ReverseSequenceOptions: + { + value = new circle::ReverseSequenceOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MatrixDiagOptions: + { + value = + new circle::MatrixDiagOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_QuantizeOptions: + { + value = new circle::QuantizeOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_MatrixSetDiagOptions: + { + value = new circle::MatrixSetDiagOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_HardSwishOptions: + { + value = + new circle::HardSwishOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_IfOptions: + { + value = new circle::IfOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_WhileOptions: + { + value = new circle::WhileOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_DepthToSpaceOptions: + { + value = new circle::DepthToSpaceOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_NonMaxSuppressionV4Options: + { + value = new circle::NonMaxSuppressionV4OptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_NonMaxSuppressionV5Options: + { + value = new circle::NonMaxSuppressionV5OptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ScatterNdOptions: + { + value = + new circle::ScatterNdOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SelectV2Options: + { + value = new circle::SelectV2OptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_DensifyOptions: + { + value = new circle::DensifyOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_SegmentSumOptions: + { + value = + new circle::SegmentSumOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BatchMatMulOptions: + { + value = + new circle::BatchMatMulOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_CumsumOptions: + { + value = new circle::CumsumOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_CallOnceOptions: + { + value = new circle::CallOnceOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BroadcastToOptions: + { + value = + new circle::BroadcastToOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_Rfft2dOptions: + { + value = new circle::Rfft2dOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_Conv3DOptions: + { + value = new circle::Conv3DOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_HashtableOptions: + { + value = + new circle::HashtableOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_HashtableFindOptions: + { + value = new circle::HashtableFindOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_HashtableImportOptions: + { + value = new circle::HashtableImportOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_HashtableSizeOptions: + { + value = new circle::HashtableSizeOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_VarHandleOptions: + { + value = + new circle::VarHandleOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_ReadVariableOptions: + { + value = new circle::ReadVariableOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_AssignVariableOptions: + { + value = new circle::AssignVariableOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_RandomOptions: + { + value = new circle::RandomOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BCQGatherOptions: + { + value = + new circle::BCQGatherOptionsT(*reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_BCQFullyConnectedOptions: + { + value = new circle::BCQFullyConnectedOptionsT( + *reinterpret_cast(u.value)); + break; + } + case BuiltinOptions_InstanceNormOptions: + { + value = new circle::InstanceNormOptionsT( + *reinterpret_cast(u.value)); + break; + } + default: + break; + } +} + +inline void BuiltinOptionsUnion::Reset() +{ + switch (type) + { + case BuiltinOptions_Conv2DOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_DepthwiseConv2DOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ConcatEmbeddingsOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LSHProjectionOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_Pool2DOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SVDFOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_RNNOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_FullyConnectedOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SoftmaxOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ConcatenationOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_AddOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_L2NormOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LocalResponseNormalizationOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LSTMOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ResizeBilinearOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_CallOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ReshapeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SkipGramOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SpaceToDepthOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_EmbeddingLookupSparseOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MulOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_PadOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_GatherOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BatchToSpaceNDOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SpaceToBatchNDOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_TransposeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ReducerOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SubOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_DivOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SqueezeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SequenceRNNOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_StridedSliceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ExpOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_TopKV2Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SplitOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LogSoftmaxOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_CastOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_DequantizeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MaximumMinimumOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ArgMaxOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LessOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_NegOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_PadV2Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_GreaterOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_GreaterEqualOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LessEqualOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SelectOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SliceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_TransposeConvOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SparseToDenseOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_TileOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ExpandDimsOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_EqualOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_NotEqualOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ShapeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_PowOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ArgMinOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_FakeQuantOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_PackOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LogicalOrOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_OneHotOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LogicalAndOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LogicalNotOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_UnpackOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_FloorDivOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SquareOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ZerosLikeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_FillOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BidirectionalSequenceRNNOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_UnidirectionalSequenceLSTMOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_FloorModOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_RangeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ResizeNearestNeighborOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_LeakyReluOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SquaredDifferenceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MirrorPadOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_AbsOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SplitVOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_UniqueOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ReverseV2Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_AddNOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_GatherNdOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_CosOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_WhereOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_RankOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ReverseSequenceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MatrixDiagOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_QuantizeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_MatrixSetDiagOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_HardSwishOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_IfOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_WhileOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_DepthToSpaceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_NonMaxSuppressionV4Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_NonMaxSuppressionV5Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ScatterNdOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SelectV2Options: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_DensifyOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_SegmentSumOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BatchMatMulOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_CumsumOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_CallOnceOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BroadcastToOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_Rfft2dOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_Conv3DOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_HashtableOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_HashtableFindOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_HashtableImportOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_HashtableSizeOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_VarHandleOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_ReadVariableOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_AssignVariableOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_RandomOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BCQGatherOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_BCQFullyConnectedOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case BuiltinOptions_InstanceNormOptions: + { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + default: + break; + } + value = nullptr; + type = BuiltinOptions_NONE; +} + +inline const circle::Model *GetModel(const void *buf) +{ + return flatbuffers::GetRoot(buf); +} + +inline const circle::Model *GetSizePrefixedModel(const void *buf) +{ + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline const char *ModelIdentifier() { return "CIR0"; } + +inline bool ModelBufferHasIdentifier(const void *buf) +{ + return flatbuffers::BufferHasIdentifier(buf, ModelIdentifier()); +} + +inline bool VerifyModelBuffer(flatbuffers::Verifier &verifier) +{ + return verifier.VerifyBuffer(ModelIdentifier()); +} + +inline bool VerifySizePrefixedModelBuffer(flatbuffers::Verifier &verifier) +{ + return verifier.VerifySizePrefixedBuffer(ModelIdentifier()); +} + +inline const char *ModelExtension() { return "circle"; } + +inline void FinishModelBuffer(flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) +{ + fbb.Finish(root, ModelIdentifier()); +} + +inline void FinishSizePrefixedModelBuffer(flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) +{ + fbb.FinishSizePrefixed(root, ModelIdentifier()); +} + +inline std::unique_ptr +UnPackModel(const void *buf, const flatbuffers::resolver_function_t *res = nullptr) +{ + return std::unique_ptr(GetModel(buf)->UnPack(res)); +} + +inline std::unique_ptr +UnPackSizePrefixedModel(const void *buf, const flatbuffers::resolver_function_t *res = nullptr) +{ + return std::unique_ptr(GetSizePrefixedModel(buf)->UnPack(res)); +} + +} // namespace circle + +#endif // FLATBUFFERS_GENERATED_SCHEMA_CIRCLE_H_ diff --git a/onert-micro/helpers/GenerateKernelsListHelper.cpp b/onert-micro/helpers/GenerateKernelsListHelper.cpp index 1420020..68b194f 100644 --- a/onert-micro/helpers/GenerateKernelsListHelper.cpp +++ b/onert-micro/helpers/GenerateKernelsListHelper.cpp @@ -88,6 +88,8 @@ std::string get_register_kernel_str(const circle::BuiltinOperator builtin_operat return "REGISTER_KERNEL(LOGICAL_OR, LogicalOr)"; case circle::BuiltinOperator_LOGISTIC: return "REGISTER_KERNEL(LOGISTIC, Logistic)"; + case circle::BuiltinOperator_GATHER: + return "REGISTER_KERNEL(GATHER, Gather)"; case circle::BuiltinOperator_MAXIMUM: return "REGISTER_KERNEL(MAXIMUM, Maximum)"; case circle::BuiltinOperator_MAX_POOL_2D: @@ -106,10 +108,14 @@ std::string get_register_kernel_str(const circle::BuiltinOperator builtin_operat return "REGISTER_KERNEL(PAD, Pad)"; case circle::BuiltinOperator_PADV2: return "REGISTER_KERNEL(PADV2, PadV2)"; + case circle::BuiltinOperator_PACK: + return "REGISTER_KERNEL(PACK, Pack)"; case circle::BuiltinOperator_PRELU: return "REGISTER_KERNEL(PRELU, PRelu)"; case circle::BuiltinOperator_QUANTIZE: return "REGISTER_KERNEL(QUANTIZE, Quantize)"; + case circle::BuiltinOperator_REDUCE_PROD: + return "REGISTER_KERNEL(REDUCE_PROD, ReduceCommon)"; case circle::BuiltinOperator_RESHAPE: return "REGISTER_KERNEL(RESHAPE, Reshape)"; case circle::BuiltinOperator_RESIZE_BILINEAR: @@ -126,6 +132,8 @@ std::string get_register_kernel_str(const circle::BuiltinOperator builtin_operat return "REGISTER_KERNEL(SPACE_TO_BATCH_ND, SpaceToBatchND)"; case circle::BuiltinOperator_SPACE_TO_DEPTH: return "REGISTER_KERNEL(SPACE_TO_DEPTH, SpaceToDepth)"; + case circle::BuiltinOperator_SLICE: + return "REGISTER_KERNEL(SLICE, Slice)"; case circle::BuiltinOperator_STRIDED_SLICE: return "REGISTER_KERNEL(STRIDED_SLICE, StridedSlice)"; case circle::BuiltinOperator_SQRT: @@ -140,6 +148,10 @@ std::string get_register_kernel_str(const circle::BuiltinOperator builtin_operat return "REGISTER_KERNEL(SUB, Sub)"; case circle::BuiltinOperator_SVDF: return "REGISTER_KERNEL(SVDF, SVDF)"; + case circle::BuiltinOperator_SPLIT: + return "REGISTER_KERNEL(SPLIT, Split)"; + case circle::BuiltinOperator_SPLIT_V: + return "REGISTER_KERNEL(SPLIT_V, SplitV)"; case circle::BuiltinOperator_TANH: return "REGISTER_KERNEL(TANH, Tanh)"; case circle::BuiltinOperator_TRANSPOSE: @@ -148,6 +160,8 @@ std::string get_register_kernel_str(const circle::BuiltinOperator builtin_operat return "REGISTER_KERNEL(TRANSPOSE_CONV, TransposeConv)"; case circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM: return "REGISTER_KERNEL(UNIDIRECTIONAL_SEQUENCE_LSTM, UnidirectionalSequenceLSTM)"; + case circle::BuiltinOperator_WHILE: + return "REGISTER_KERNEL(WHILE, While)"; default: assert(false && "Not supported kernel"); } diff --git a/onert-micro/luci-interpreter/CMakeLists.txt b/onert-micro/luci-interpreter/CMakeLists.txt index b07ded2..1bdfa49 100644 --- a/onert-micro/luci-interpreter/CMakeLists.txt +++ b/onert-micro/luci-interpreter/CMakeLists.txt @@ -1,5 +1,6 @@ set(LUCI_INTERPRETER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") set(LUCI_INTERPRETER_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") +set(LUCI_INTERPRETER_PAL_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pal/common") if (NOT LUCI_INTERPRETER_PAL_DIR) set(LUCI_INTERPRETER_PAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pal/mcu") endif() @@ -26,4 +27,11 @@ endif() add_compile_options(-fno-exceptions) add_compile_options(-Os) + +# AFAIK, this will enable leak sanitizer, too +if(ENABLE_SANITIZER) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif(ENABLE_SANITIZER) + add_subdirectory(src) diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/Interpreter.h b/onert-micro/luci-interpreter/include/luci_interpreter/Interpreter.h index 483c789..0d29475 100644 --- a/onert-micro/luci-interpreter/include/luci_interpreter/Interpreter.h +++ b/onert-micro/luci-interpreter/include/luci_interpreter/Interpreter.h @@ -36,7 +36,7 @@ class Interpreter { public: // Construct default interpreter with dynamic allocations and with input allocations - explicit Interpreter(const char *model_data_raw); + explicit Interpreter(const char *model_data_raw, bool dealloc_input); #ifdef USE_STATIC_ALLOC // Construct interpreter with configurations diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/TrainingSettings.h b/onert-micro/luci-interpreter/include/luci_interpreter/TrainingSettings.h new file mode 100644 index 0000000..40eae7e --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/TrainingSettings.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef ENABLE_TRAINING + +#ifndef LUCI_INTERPRETER_TRAINING_SETTINGS_H +#define LUCI_INTERPRETER_TRAINING_SETTINGS_H + +#include + +namespace luci_interpreter +{ + +namespace training +{ + +enum Status +{ + Ok, + Error, + EnableTrainModeError, + DoubleTrainModeError +}; + +enum MetricsTypeEnum +{ + MSE, + MAE +}; + +enum LossTypeEnum +{ + MSE_Loss +}; + +enum OptimizerTypeEnum +{ + SGD +}; + +struct TrainingSettings +{ + MetricsTypeEnum metric = MSE; + LossTypeEnum error_type = MSE_Loss; + OptimizerTypeEnum optimizer_type = SGD; + uint32_t number_of_epochs = 1; + uint32_t batch_size = 1; + float learning_rate = 0.00001; + uint32_t number_of_last_trainable_layers = 1; +}; + +} // namespace training +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TRAINING_SETTINGS_H + +#endif // ENABLE_TRAINING diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h b/onert-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h index 318ec0e..37c9ed9 100644 --- a/onert-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h +++ b/onert-micro/luci-interpreter/include/luci_interpreter/core/Tensor.h @@ -30,6 +30,106 @@ namespace luci_interpreter { +static constexpr int kMaxSmallSize = 5; + +class RuntimeShape +{ +public: + RuntimeShape(const RuntimeShape &other) : _size(other.dimensionsCount()) + { + std::memcpy(dimsData(), other.dimsData(), sizeof(int32_t) * _size); + } + + // Returns the total count of elements, that is the size when flattened into a + // vector. + inline int flatSize() const + { + int buffer_size = 1; + const int *dims_data = reinterpret_cast(dimsData()); + for (int i = 0; i < _size; i++) + { + buffer_size *= dims_data[i]; + } + return buffer_size; + } + + inline int32_t *dimsData() { return _dims; } + inline const int32_t *dimsData() const { return _dims; } + + RuntimeShape() : _size(0) {} + + explicit RuntimeShape(int dimensions_count) : _size(dimensions_count) + { + assert(dimensions_count <= kMaxSmallSize); + assert(dimensions_count >= 0); + } + + RuntimeShape(int dimensions_count, const int32_t *dims_data) : _size(0) + { + resize(dimensions_count); + int32_t *dst_dims = dimsData(); + std::memcpy(dst_dims, dims_data, dimensions_count * sizeof(int32_t)); + } + + RuntimeShape(int new_shape_size, const RuntimeShape &shape, int pad_value) : _size(0) + { + resize(new_shape_size); + const int size_increase = new_shape_size - shape.dimensionsCount(); + for (int i = 0; i < size_increase; ++i) + { + setDim(i, pad_value); + } + std::memcpy(dimsData() + size_increase, shape.dimsData(), + sizeof(int32_t) * shape.dimensionsCount()); + } + + RuntimeShape(int shape_size, int32_t value) : _size(0) + { + resize(shape_size); + for (int i = 0; i < shape_size; ++i) + { + setDim(i, value); + } + } + + inline static RuntimeShape extendedShape(int new_shape_size, const RuntimeShape &shape) + { + return RuntimeShape(new_shape_size, shape, 1); + } + + bool operator==(const RuntimeShape &comp) const + { + return this->_size == comp._size && + std::memcmp(dimsData(), comp.dimsData(), _size * sizeof(int32_t)) == 0; + } + + inline int32_t dimensionsCount() const { return _size; } + + inline int32_t dims(int i) const + { + assert(i <= _size); + assert(i >= 0); + return _dims[i]; + } + inline void setDim(int i, int32_t val) + { + assert(i <= _size); + assert(i >= 0); + _dims[i] = val; + } + + inline void resize(int dimensions_count) + { + assert(dimensions_count <= kMaxSmallSize); + assert(dimensions_count >= 0); + _size = dimensions_count; + } + +private: + int32_t _size; + int32_t _dims[kMaxSmallSize]; +}; + class Tensor { public: @@ -99,15 +199,26 @@ public: } #endif + static bool is_constant_tensor(const luci_interpreter::CircleReader *reader, + const circle::Tensor *circle_tensor) + { + return reader->buffers()[circle_tensor->buffer()]->data() != nullptr; + } + static DataType element_type(const circle::Tensor *circle_tensor) { return luci_datatype(circle_tensor->type()); } + static VectorWrapper tensor_shape(const circle::Tensor *circle_tensor) + { + return wrap(circle_tensor->shape()); + } + static int num_dims(const circle::Tensor *circle_tensor) { // TODO check removing of wrap - auto const &const_dims = wrap(circle_tensor->shape()); + auto const const_dims = wrap(circle_tensor->shape()); return const_dims.size(); } @@ -115,7 +226,7 @@ public: { // TODO check removing of wrap assert(i >= 0); - auto const &const_dims = wrap(circle_tensor->shape()); + auto const const_dims = wrap(circle_tensor->shape()); assert(i < const_dims.size()); return const_dims[i]; @@ -124,7 +235,7 @@ public: static int32_t num_elements(const circle::Tensor *circle_tensor) { int32_t result = 1; - auto const &const_dims = wrap(circle_tensor->shape()); + auto const const_dims = wrap(circle_tensor->shape()); for (const int32_t dim : const_dims) { result *= dim; diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/core/reader/CircleMicroReader.h b/onert-micro/luci-interpreter/include/luci_interpreter/core/reader/CircleMicroReader.h index 3967e40..14021fc 100644 --- a/onert-micro/luci-interpreter/include/luci_interpreter/core/reader/CircleMicroReader.h +++ b/onert-micro/luci-interpreter/include/luci_interpreter/core/reader/CircleMicroReader.h @@ -179,10 +179,12 @@ public: // direct API public: bool parse(const circle::Model *model); bool select_subgraph(uint32_t subgraph); + uint32_t get_current_subgraph_index() const { return _current_subgraph_index; } private: const circle::Model *_model{nullptr}; const circle::SubGraph *_current_subgraph{nullptr}; + uint32_t _current_subgraph_index{0}; }; } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/onert-micro-version.h b/onert-micro/luci-interpreter/include/luci_interpreter/onert-micro-version.h new file mode 100644 index 0000000..5a38396 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/onert-micro-version.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __ONERT_MICRO_VERSION_H__ +#define __ONERT_MICRO_VERSION_H__ + +/** + * ONERT_MICRO_VERSION is a uint32 value representing onert-micro version + * in 0xMMmmmmPP, where MM = major, mmmm = minor, PP = patch + */ +#define ONERT_MICRO_VERSION 0x01000000 + +#endif // __ONERT_MICRO_VERSION_H__ diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/TestDataBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/TestDataBase.h new file mode 100644 index 0000000..9cd2148 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/TestDataBase.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_TEST_DATA_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_TEST_DATA_BASE_H + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataBase +{ +public: + virtual ~TestDataBase() = default; + + virtual const unsigned char *get_model_ptr() = 0; + + virtual const std::vector &get_input_data_by_index(int i) = 0; + virtual const std::vector &get_output_data_by_index(int i) = 0; +}; + +class NegTestDataBase +{ +public: + virtual ~NegTestDataBase() = default; + virtual const unsigned char *get_model_ptr() = 0; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_TEST_DATA_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/FloatAbsKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/FloatAbsKernel.h new file mode 100644 index 0000000..a7e8ccc --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/FloatAbsKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_ABS_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_ABS_KERNEL_H + +#include "TestDataAbsBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace abs_float +{ +/* + * Abs Kernel: + * + * Input(1, 3, 3, 2) + * | + * Abs + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {4.5279765, -6.575015, -10.009525, 3.1054533, -0.49870253, + -9.601274, 8.061923, 8.651763, 5.580226, -22.497627, + -5.331085, -0.5524021, -11.368782, -0.61816937, 11.072669, + -10.092069, 7.357945, 8.606385}; + +const std::vector reference_output_data = { + 4.5279765, 6.575015, 10.009525, 3.1054533, 0.49870253, 9.601274, 8.061923, 8.651763, 5.580226, + 22.497627, 5.331085, 0.5524021, 11.368782, 0.61816937, 11.072669, 10.092069, 7.357945, 8.606385}; + +} // namespace abs_float + +class TestDataFloatAbs : public TestDataAbsBase +{ +public: + TestDataFloatAbs() + { + _input_data = abs_float::input_data; + _reference_output_data = abs_float::reference_output_data; + _test_kernel_model_circle = abs_float::test_kernel_model_circle; + } + + ~TestDataFloatAbs() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_ABS_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/NegAbsKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/NegAbsKernel.h new file mode 100644 index 0000000..aaf167f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/NegAbsKernel.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ABS_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ABS_KERNEL_H + +#include "TestDataAbsBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_abs_input_output_type_mismatch +{ +/* + * Abs Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * Abs + * | + * Output(1, 3, 3, 2) - Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_abs_input_output_type_mismatch + +namespace neg_abs_input_output_shape_mismatch +{ +/* + * Abs Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * Abs + * | + * Output(3, 3, 2) - Float + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x65, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_abs_input_output_shape_mismatch + +class NegTestDataInputOutputTypeMismatchAbsKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchAbsKernel() + { + _test_kernel_model_circle = neg_abs_input_output_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchAbsKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInputOutputShapeMismatchAbsKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputShapeMismatchAbsKernel() + { + _test_kernel_model_circle = neg_abs_input_output_shape_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputOutputShapeMismatchAbsKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_ABS_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/TestDataAbsBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/TestDataAbsBase.h new file mode 100644 index 0000000..1d7fa58 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/abs/TestDataAbsBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ABS_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ABS_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataAbsBase : public TestDataBase +{ +public: + TestDataAbsBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ABS_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/FloatAddKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/FloatAddKernel.h new file mode 100644 index 0000000..5f9e742 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/FloatAddKernel.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_FLOAT_H + +#include "TestDataAddBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace add_float_with_broadcasting +{ + +/* + * Add Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Add(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_add_kernel_float_with_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0xfe, 0xff, 0xff, 0xa0, 0xfe, 0xff, 0xff, 0xa4, 0xfe, 0xff, 0xff, 0xa8, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {11.547888, 16.922003, 23.412094, 7.1120033, -1.9457912, + 26.603596, -13.668177, 16.682764, 21.436306, 11.578255}; +const std::vector input2_data = {-18.080006, 4.956518}; +const std::vector reference_output_data = {-6.532118, -1.1580029, 5.3320885, -10.968002, + -20.025797, 31.560114, -8.7116585, 21.639282, + 26.392824, 16.534773}; +} // namespace add_float_with_broadcasting + +namespace add_float_no_broadcasting +{ +/* + * Add Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Add(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_add_kernel_float_no_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0xfe, 0xff, 0xff, 0xa0, 0xfe, 0xff, 0xff, 0xa4, 0xfe, 0xff, 0xff, 0xa8, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {-18.994303, -21.966125, 13.298149, 14.595678, 14.874681, + -7.8676147, 18.542614, 12.96068, 17.352306, 1.6868477}; +std::vector input2_data = {13.860439, 7.7335033, 8.465873, 6.483177, 12.05286, + -14.429752, 47.21386, -14.103956, 23.698446, 28.710766}; +std::vector reference_output_data = {-5.1338634, -14.232622, 21.764023, 21.078856, + 26.927542, -22.297367, 65.75647, -1.1432762, + 41.05075, 30.397614}; +} // namespace add_float_no_broadcasting + +class TestDataFloatAdd : public TestDataAddBase +{ +public: + explicit TestDataFloatAdd(bool is_with_broadcast) : TestDataAddBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = add_float_with_broadcasting::input1_data; + _input2_data = add_float_with_broadcasting::input2_data; + _reference_output_data = add_float_with_broadcasting::reference_output_data; + _test_add_kernel_model_circle = + add_float_with_broadcasting::test_add_kernel_float_with_broadcasting_model_circle; + } + else + { + _input1_data = add_float_no_broadcasting::input1_data; + _input2_data = add_float_no_broadcasting::input2_data; + _reference_output_data = add_float_no_broadcasting::reference_output_data; + _test_add_kernel_model_circle = + add_float_no_broadcasting::test_add_kernel_float_no_broadcasting_model_circle; + } + } + + ~TestDataFloatAdd() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/IntAddKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/IntAddKernel.h new file mode 100644 index 0000000..6bc73ff --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/IntAddKernel.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_INT_H + +#include "TestDataAddBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace add_int_with_broadcasting +{ + +/* + * Add Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Add(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_add_kernel_int32_with_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0xfe, 0xff, 0xff, 0x94, 0xfe, 0xff, 0xff, 0x98, 0xfe, 0xff, 0xff, 0x9c, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const unsigned char test_add_kernel_int64_with_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x32, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-5, 5, 5, 5, -13, -5, -3, 21, 5, -4}; +const std::vector input2_data = {6, -14}; +const std::vector reference_output_data = {1, 11, 11, 11, -7, -19, -17, 7, -9, -18}; + +const std::vector input1_data_32 = {-5, 5, 5, 5, -13, -5, -3, 21, 5, -4}; +const std::vector input2_data_32 = {6, -14}; +const std::vector reference_output_data_32 = {1, 11, 11, 11, -7, -19, -17, 7, -9, -18}; +} // namespace add_int_with_broadcasting + +namespace add_int_no_broadcasting +{ +/* + * Add Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Add(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_add_kernel_int32_no_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0xfe, 0xff, 0xff, 0x94, 0xfe, 0xff, 0xff, 0x98, 0xfe, 0xff, 0xff, 0x9c, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const unsigned char test_add_kernel_int64_no_broadcasting_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x32, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x41, 0x64, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {5, -5, -3, 15, 15, -11, 13, 6, 15, 15}; +std::vector input2_data = {15, 5, 5, 15, 7, 4, 13, 5, 6, 13}; +std::vector reference_output_data = {20, 0, 2, 30, 22, -7, 26, 11, 21, 28}; + +std::vector input1_data_32 = {5, -5, -3, 15, 15, -11, 13, 6, 15, 15}; +std::vector input2_data_32 = {15, 5, 5, 15, 7, 4, 13, 5, 6, 13}; +std::vector reference_output_data_32 = {20, 0, 2, 30, 22, -7, 26, 11, 21, 28}; + +} // namespace add_int_no_broadcasting + +class TestData64IntAdd : public TestDataAddBase +{ +public: + explicit TestData64IntAdd(bool is_with_broadcast) : TestDataAddBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = add_int_with_broadcasting::input1_data; + _input2_data = add_int_with_broadcasting::input2_data; + _reference_output_data = add_int_with_broadcasting::reference_output_data; + _test_add_kernel_model_circle = + add_int_with_broadcasting::test_add_kernel_int64_with_broadcasting_model_circle; + } + else + { + _input1_data = add_int_no_broadcasting::input1_data; + _input2_data = add_int_no_broadcasting::input2_data; + _reference_output_data = add_int_no_broadcasting::reference_output_data; + _test_add_kernel_model_circle = + add_int_no_broadcasting::test_add_kernel_int64_no_broadcasting_model_circle; + } + } + + ~TestData64IntAdd() override = default; +}; + +class TestData32IntAdd : public TestDataAddBase +{ +public: + explicit TestData32IntAdd(bool is_with_broadcast) : TestDataAddBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = add_int_with_broadcasting::input1_data_32; + _input2_data = add_int_with_broadcasting::input2_data_32; + _reference_output_data = add_int_with_broadcasting::reference_output_data_32; + _test_add_kernel_model_circle = + add_int_with_broadcasting::test_add_kernel_int32_with_broadcasting_model_circle; + } + else + { + _input1_data = add_int_no_broadcasting::input1_data_32; + _input2_data = add_int_no_broadcasting::input2_data_32; + _reference_output_data = add_int_no_broadcasting::reference_output_data_32; + _test_add_kernel_model_circle = + add_int_no_broadcasting::test_add_kernel_int32_no_broadcasting_model_circle; + } + } + + ~TestData32IntAdd() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/NegAddKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/NegAddKernel.h new file mode 100644 index 0000000..44af3af --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/NegAddKernel.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ADD_KERNEl_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ADD_KERNEL_H + +#include "TestDataAddBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_add_input_type_mismatch +{ +/* + * Add Kernel with mismatching input type: + * + * Input_1(1, 4, 4) - Int32 Input_2(1, 4, 4) - Float32 + * \ / + * Add(no broadcast) + * | + * Output(1, 4, 4) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0xfe, 0xff, 0xff, 0xa0, 0xfe, 0xff, 0xff, 0xa4, 0xfe, 0xff, 0xff, 0xa8, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_add_input_type_mismatch + +namespace neg_add_no_quant_params +{ +/* + * Add Kernel in int16 type without quant params: + * + * Input_1(1, 4, 4) - Int16 Input_2(1, 4, 4) - Float16 + * \ / + * Add(no broadcast, no quant params) + * | + * Output(1, 4, 4) - Int16 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa0, 0xfe, 0xff, 0xff, 0xa4, 0xfe, 0xff, 0xff, 0xa8, 0xfe, 0xff, 0xff, 0xac, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_add_no_quant_params + +class NegTestDataInputMismatchAddKernel : public NegTestDataBase +{ +public: + NegTestDataInputMismatchAddKernel() + { + _test_kernel_model_circle = neg_add_input_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputMismatchAddKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataNoQuantParamsS16AddKernel : public NegTestDataBase +{ +public: + NegTestDataNoQuantParamsS16AddKernel() + { + _test_kernel_model_circle = neg_add_no_quant_params::test_kernel_model_circle; + } + + ~NegTestDataNoQuantParamsS16AddKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_U8_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/TestDataAddBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/TestDataAddBase.h new file mode 100644 index 0000000..8ffa7fb --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add/TestDataAddBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataAddBase : public TestDataBase +{ +public: + explicit TestDataAddBase(bool) + { + // Do nothing + } + + TestDataAddBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_add_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_add_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/FloatAddNKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/FloatAddNKernel.h new file mode 100644 index 0000000..95fd966 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/FloatAddNKernel.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_FLOAT_H + +#include "TestDataAddNBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace add_n_float +{ +/* + * AddN Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ | / + * AddN + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + 6.427741, -5.5336685, -6.2939873, 8.121177, -3.0810785, -10.091913, -2.4849992, 1.774254, + 13.800129, -22.152988, 12.696625, 0.2929567, 8.354922, 5.7603703, -5.8994074, -18.484707, + 8.926415, 15.341654, 4.8836274, 5.289197, -9.766411, 13.688, -2.9341066, 7.2281685, + -7.639269, -19.849337, -10.183603, -3.39605, 7.0884247, -4.100115, -13.114395, 20.1859, + 8.514902, -2.8839726, -4.8524947, -7.7189302, 12.958817, 0.7408314, -14.70222, -4.840035, + -5.6485643, 9.197558, 4.805386, -5.6769075, -4.0590677, -6.5562315, 7.0200677, -0.99683046}; +const std::vector input2_data = { + -4.0646977, 3.8888195, -0.45402065, -2.352005, 4.839372, -16.821068, 6.937857, 6.233658, + 16.912395, -7.1494417, 6.260419, -0.9814551, 5.560984, 2.5352159, 3.3039222, -13.475629, + 21.22035, -14.078774, 5.582642, 4.1715817, -3.3241076, 1.4076965, -1.1146233, -5.846616, + -9.14507, -7.9248514, 3.61239, -4.173052, 1.4289827, 1.0473942, -8.506401, -11.117105, + 11.395946, -3.0757384, -13.336702, 1.6729355, 9.1125765, -5.3872676, -17.386013, 1.4701926, + -16.397867, 9.311203, -9.718552, -8.854298, 8.296376, 3.8650365, 8.381851, -6.6090994}; +const std::vector input3_data = { + 11.190196, 5.642186, -7.297735, 11.227684, -1.727619, 1.9045501, -9.593952, -16.171299, + -6.0474806, 3.3553686, 19.021252, -3.9855165, 5.2290893, 8.515632, -8.236364, 12.097031, + 7.9482317, 1.4470768, -0.58474195, -2.9820383, 9.381822, 8.335634, 0.9053579, -0.120785415, + 7.994109, -7.4182167, 9.492107, 0.7696781, 6.868584, 6.2453837, 1.7782576, 5.5902786, + 9.0994215, -8.651535, -0.6730907, 1.4408729, 5.3254695, 4.124748, 8.724231, -6.1463547, + 2.751103, -4.8675337, -9.386753, -15.851856, 5.0927544, 0.2861572, -2.6495001, -4.626466}; +const std::vector reference_output_data = { + 13.55324, 3.997337, -14.045743, 16.996857, 0.030674577, -25.00843, -5.141094, -8.163387, + 24.665043, -25.94706, 37.978294, -4.674015, 19.144997, 16.811218, -10.83185, -19.863304, + 38.094997, 2.7099562, 9.881528, 6.4787407, -3.7086973, 23.431332, -3.143372, 1.2607672, + -8.790231, -35.192406, 2.9208941, -6.7994237, 15.385992, 3.1926632, -19.842539, 14.659074, + 29.01027, -14.611246, -18.862288, -4.6051216, 27.396862, -0.521688, -23.364002, -9.516197, + -19.29533, 13.641229, -14.299919, -30.38306, 9.330063, -2.4050379, 12.7524185, -12.232395}; + +} // namespace add_n_float + +class TestDataFloatAddN : public TestDataAddNBase +{ +public: + TestDataFloatAddN() + { + _input1_data = add_n_float::input1_data; + _input2_data = add_n_float::input2_data; + _input3_data = add_n_float::input3_data; + _reference_output_data = add_n_float::reference_output_data; + _test_kernel_model_circle = add_n_float::test_kernel_model_circle; + } + + ~TestDataFloatAddN() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/NegAddNKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/NegAddNKernel.h new file mode 100644 index 0000000..762edd4 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/NegAddNKernel.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ADD_N_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ADD_N_KERNEL_H + +#include "TestDataAddNBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace input_type_mismatch_add_n +{ +/* + * AddN Kernel with input1 type != input2 type: + * + * Input_1(1, 4, 4, 3)-Float Input_2(1, 4, 4, 3) Input_2(1, 4, 4, 3) - Int32 + * \ | / + * AddN + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x52, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace input_type_mismatch_add_n + +class TestDataInputTypeMismatchAddN : public NegTestDataBase +{ +public: + TestDataInputTypeMismatchAddN() + { + _test_kernel_model_circle = input_type_mismatch_add_n::test_kernel_model_circle; + } + + ~TestDataInputTypeMismatchAddN() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_ADD_N_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/TestDataAddNBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/TestDataAddNBase.h new file mode 100644 index 0000000..6b07845 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/add_n/TestDataAddNBase.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataAddNBase : public TestDataBase +{ +public: + TestDataAddNBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + case 2: + return _input3_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _input3_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ADD_N_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/FloatArgMaxKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/FloatArgMaxKernel.h new file mode 100644 index 0000000..9d9be08 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/FloatArgMaxKernel.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_FLOAT_H + +#include "TestDataArgMaxBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace argmax_float +{ + +/* + * ArgMax Kernel: + * + * Input_1(4, 5) Input_2(scalar = 1) + * \ / + * ArgMax + * | + * Output(4) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x61, 0x72, 0x67, 0x6d, + 0x61, 0x78, 0x2f, 0x64, 0x69, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + 16.19906, 15.062998, 19.666464, -20.029007, -15.745727, 0.014214589, -6.178691, + 7.236639, -4.1050725, 15.853367, -4.012241, -11.736127, -0.40098614, -17.605135, + -7.4517574, -0.11448864, 13.045483, 7.009659, 5.189774, -6.1986547}; + +const std::vector reference_output_data = {2, 4, 2, 1}; + +} // namespace argmax_float + +class TestDataFloatArgMax : public TestDataArgMaxBase +{ +public: + TestDataFloatArgMax() + { + _input1_data = argmax_float::input1_data; + _reference_output_data = argmax_float::reference_output_data; + _test_kernel_model_circle = argmax_float::test_kernel_model_circle; + } + + ~TestDataFloatArgMax() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/NegArgMaxKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/NegArgMaxKernel.h new file mode 100644 index 0000000..9d6aff6 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/NegArgMaxKernel.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MAX_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MAX_KERNEL_H + +#include "TestDataArgMaxBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace wrong_output_type_arg_max +{ + +/* + * ArgMax Kernel with wrong output type (Float32): + * + * Input_1(4, 5) Input_2(scalar = 1) + * \ / + * ArgMax + * | + * Output(4) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x61, 0x72, 0x67, 0x6d, 0x61, 0x78, 0x2f, 0x64, + 0x69, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace wrong_output_type_arg_max + +class TestDataOutputWrongOutputArgMax : public NegTestDataBase +{ +public: + TestDataOutputWrongOutputArgMax() + { + _test_kernel_model_circle = wrong_output_type_arg_max::test_kernel_model_circle; + } + + ~TestDataOutputWrongOutputArgMax() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MAX_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/TestDataArgMaxBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/TestDataArgMaxBase.h new file mode 100644 index 0000000..5100d49 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmax/TestDataArgMaxBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataArgMaxBase : public TestDataBase +{ +public: + TestDataArgMaxBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ARG_MAX_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/FloatArgMinKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/FloatArgMinKernel.h new file mode 100644 index 0000000..0b915ae --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/FloatArgMinKernel.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_FLOAT_H + +#include "TestDataArgMinBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace argmin_float +{ + +/* + * ArgMin Kernel: + * + * Input_1(4, 5) Input_2(scalar = 1) + * \ / + * ArgMin + * | + * Output(4) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x39, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x61, 0x72, 0x67, 0x6d, + 0x69, 0x6e, 0x2f, 0x64, 0x69, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + 16.19906, 15.062998, 19.666464, -20.029007, -15.745727, 0.014214589, -6.178691, + 7.236639, -4.1050725, 15.853367, -4.012241, -11.736127, -0.40098614, -17.605135, + -7.4517574, -0.11448864, 13.045483, 7.009659, 5.189774, -6.1986547}; + +const std::vector reference_output_data = {3, 1, 3, 4}; + +} // namespace argmin_float + +class TestDataFloatArgMin : public TestDataArgMinBase +{ +public: + TestDataFloatArgMin() + { + _input1_data = argmin_float::input1_data; + _reference_output_data = argmin_float::reference_output_data; + _test_kernel_model_circle = argmin_float::test_kernel_model_circle; + } + + ~TestDataFloatArgMin() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/NegArgMinKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/NegArgMinKernel.h new file mode 100644 index 0000000..0eaeb66 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/NegArgMinKernel.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MIN_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MIN_KERNEL_H + +#include "TestDataArgMinBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace wrong_output_type_arg_min +{ + +/* + * ArgMin Kernel with wrong output type (Float32): + * + * Input_1(4, 5) Input_2(scalar = 1) + * \ / + * ArgMin + * | + * Output(4) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x61, 0x72, 0x67, 0x6d, + 0x69, 0x6e, 0x2f, 0x64, 0x69, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace wrong_output_type_arg_min + +class TestDataOutputWrongOutputArgMin : public NegTestDataBase +{ +public: + TestDataOutputWrongOutputArgMin() + { + _test_kernel_model_circle = wrong_output_type_arg_min::test_kernel_model_circle; + } + + ~TestDataOutputWrongOutputArgMin() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_ARG_MIN_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/TestDataArgMinBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/TestDataArgMinBase.h new file mode 100644 index 0000000..8ae22c4 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/argmin/TestDataArgMinBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataArgMinBase : public TestDataBase +{ +public: + TestDataArgMinBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ARG_MIN_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/FloatAveragePool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/FloatAveragePool2DKernel.h new file mode 100644 index 0000000..58790e9 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/FloatAveragePool2DKernel.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_AVERAGE_POOL2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_AVERAGE_POOL2D_KERNEL_H + +#include "TestDataAveragePool2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace average_pool2d_float +{ +/* + * AveragePool2D Kernel: + * + * Input(1, 8, 8, 1) + * | + * AveragePool2D + * | + * Output(1, 7, 7, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -10.829727, -5.0753784, -0.46581638, 1.1337309, -12.983028, -1.7739874, -3.8115792, -1.470794, + 0.8652655, 3.5751436, 3.142009, -2.881177, 0.21681504, -0.6201232, -1.168152, -5.972758, + 6.4394593, 0.60464424, -1.8141485, -17.769108, -13.40761, 6.139243, -2.3543136, -2.5557704, + 15.057343, 7.4924536, -20.035614, 4.250232, 5.9063106, 10.382995, -7.45354, 3.7568998, + 5.0376787, 2.825182, -7.3617344, -3.2233214, 2.2610564, -6.776909, -2.56466, 17.584259, + 15.771288, 5.9487047, -0.11435696, 8.510494, 9.547339, 11.753286, 12.103353, -14.300014, + 4.453389, 11.3001, -7.494295, 9.240987, -2.8403296, -1.9216467, 8.1578245, -7.334697, + 1.2287734, 0.7231084, 9.715425, -7.466359, -15.67608, 6.574766, 4.489766, -1.6495954}; + +const std::vector reference_output_data{ + -2.866174, 0.29398948, 0.23218668, -3.6284149, -3.790081, -1.8434604, -3.1058207, + 2.871128, 1.376912, -4.830606, -8.46027, -1.9179188, 0.4991635, -3.0127485, + 7.3984747, -3.4381661, -8.842159, -5.2550435, 2.2552347, 1.678596, -2.151681, + 7.6031637, -4.269928, -6.5926094, 2.2985694, 2.9433632, -1.6030285, 2.83074, + 7.395714, 0.32444882, -0.54722965, 4.273892, 4.196193, 3.6287675, 3.2057345, + 9.368371, 2.4100385, 2.535707, 6.1146226, 4.134662, 7.523204, -0.3433833, + 4.426343, 3.5610845, 0.9989393, -4.1854453, -3.4658222, 4.3251777, 0.91582465}; + +} // namespace average_pool2d_float + +class TestDataFloatAveragePool2D : public TestDataAveragePool2DBase +{ +public: + TestDataFloatAveragePool2D() + { + _input_data = average_pool2d_float::input_data; + _reference_output_data = average_pool2d_float::reference_output_data; + _test_kernel_model_circle = average_pool2d_float::test_kernel_model_circle; + } + + ~TestDataFloatAveragePool2D() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_AVERAGE_POOL2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/NegAveragePool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/NegAveragePool2DKernel.h new file mode 100644 index 0000000..0c1c5a2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/NegAveragePool2DKernel.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_AVERAGE_POOL2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_AVERAGE_POOL2D_KERNEL_H + +#include "TestDataAveragePool2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_average_pool2d_kernel +{ +/* + * AveragePool2D Kernel with input_type != output_type: + * + * Input(1, 8, 8, 1) = Float32 + * | + * AveragePool2D + * | + * Output(1, 7, 7, 1) = Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_average_pool2d_kernel + +class NegTestDataInputOutputTypeMismatchAveragePool2DKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchAveragePool2DKernel() + { + _test_kernel_model_circle = neg_average_pool2d_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchAveragePool2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_AVERAGE_POOL2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/TestDataAveragePool2DBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/TestDataAveragePool2DBase.h new file mode 100644 index 0000000..c7de059 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/average_pool_2d/TestDataAveragePool2DBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_AVERAGE_POOL_2D_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_AVERAGE_POOL_2D_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataAveragePool2DBase : public TestDataBase +{ +public: + TestDataAveragePool2DBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_AVERAGE_POOL_2D_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/FloatConcatenationKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/FloatConcatenationKernel.h new file mode 100644 index 0000000..2908bef --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/FloatConcatenationKernel.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_FLOAT_H + +#include "TestDataConcatenationBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace concatenation_float +{ + +/* + * Concatenation Kernel: + * + * Input_1(1, 4, 4, 1) Input_2(1, 4, 4, 2) + * \ / + * Concatenation + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -2.0978436, -25.836285, 7.9663463, -52.951416, -9.174321, -10.963295, 1.4337301, -39.000927, + -11.76153, -24.070623, -21.426125, -32.041714, -2.29292, -7.595441, -15.297459, -20.068735}; +const std::vector input2_data = { + -23.881905, -8.470397, -52.49611, -21.756306, 1.581161, -26.177217, -39.463478, -2.172443, + -30.588694, -7.90017, -17.604687, -27.376356, -26.49272, -15.772057, 8.418157, -36.710365, + -35.77088, -27.592611, -5.0617495, -7.8632812, 10.318075, -33.451294, -53.1594, -33.312737, + -6.132754, -21.647987, -38.427383, -41.75349, -22.417152, -1.243468, -19.772722, -55.897236}; +const std::vector reference_output_data = { + -2.0978436, -23.881905, -8.470397, -25.836285, -52.49611, -21.756306, 7.9663463, 1.581161, + -26.177217, -52.951416, -39.463478, -2.172443, -9.174321, -30.588694, -7.90017, -10.963295, + -17.604687, -27.376356, 1.4337301, -26.49272, -15.772057, -39.000927, 8.418157, -36.710365, + -11.76153, -35.77088, -27.592611, -24.070623, -5.0617495, -7.8632812, -21.426125, 10.318075, + -33.451294, -32.041714, -53.1594, -33.312737, -2.29292, -6.132754, -21.647987, -7.595441, + -38.427383, -41.75349, -15.297459, -22.417152, -1.243468, -20.068735, -19.772722, -55.897236}; + +} // namespace concatenation_float + +class TestDataFloatConcatenation : public TestDataConcatenationBase +{ +public: + TestDataFloatConcatenation() + { + _input1_data = concatenation_float::input1_data; + _input2_data = concatenation_float::input2_data; + _reference_output_data = concatenation_float::reference_output_data; + _test_kernel_model_circle = concatenation_float::test_kernel_model_circle; + } + + ~TestDataFloatConcatenation() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/IntConcatenationKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/IntConcatenationKernel.h new file mode 100644 index 0000000..fe0b7f5 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/IntConcatenationKernel.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_INT_H + +#include "TestDataConcatenationBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace concatenation_int32 +{ + +/* + * Concatenation Kernel: + * + * Input_1(1, 4, 4, 1) Input_2(1, 4, 4, 2) + * \ / + * Concatenation + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-9, -22, -32, 7, -23, -8, -23, -32, + -31, -25, -8, -22, -23, 1, -24, -32}; +const std::vector input2_data = {-29, -31, -8, -23, 16, -23, -38, 7, -36, -22, -32, + -24, -23, -18, -33, -23, -38, -24, -38, -14, -16, -13, + -15, -22, -38, -53, -5, -40, -23, -22, -23, -41}; +const std::vector reference_output_data = { + -9, -29, -31, -22, -8, -23, -32, 16, -23, 7, -38, 7, -23, -36, -22, -8, + -32, -24, -23, -23, -18, -32, -33, -23, -31, -38, -24, -25, -38, -14, -8, -16, + -13, -22, -15, -22, -23, -38, -53, 1, -5, -40, -24, -23, -22, -32, -23, -41}; + +} // namespace concatenation_int32 + +namespace concatenation_int64 +{ + +/* + * Concatenation Kernel: + * + * Input_1(1, 4, 4, 1) Input_2(1, 4, 4, 2) + * \ / + * Concatenation + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {35, 35, 35, 35, 30, 40, 45, 30, + 35, 35, 35, 40, 35, 30, 25, 30}; +const std::vector input2_data = {25, 35, 35, 30, 40, 35, 30, 35, 35, 35, 40, + 25, 35, 30, 40, 35, 35, 35, 30, 30, 35, 35, + 45, 50, 45, 35, 35, 40, 35, 35, 30, 35}; +const std::vector reference_output_data = { + 35, 25, 35, 35, 35, 30, 35, 40, 35, 35, 30, 35, 30, 35, 35, 40, 40, 25, 45, 35, 30, 30, 40, 35, + 35, 35, 35, 35, 30, 30, 35, 35, 35, 40, 45, 50, 35, 45, 35, 30, 35, 40, 25, 35, 35, 30, 30, 35}; + +} // namespace concatenation_int64 + +class TestDataS32Concatenation : public TestDataConcatenationBase +{ +public: + TestDataS32Concatenation() + { + _input1_data = concatenation_int32::input1_data; + _input2_data = concatenation_int32::input2_data; + _reference_output_data = concatenation_int32::reference_output_data; + _test_kernel_model_circle = concatenation_int32::test_kernel_model_circle; + } + + ~TestDataS32Concatenation() override = default; +}; + +class TestDataS64Concatenation : public TestDataConcatenationBase +{ +public: + TestDataS64Concatenation() + { + _input1_data = concatenation_int64::input1_data; + _input2_data = concatenation_int64::input2_data; + _reference_output_data = concatenation_int64::reference_output_data; + _test_kernel_model_circle = concatenation_int64::test_kernel_model_circle; + } + + ~TestDataS64Concatenation() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/NegConcatenationKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/NegConcatenationKernel.h new file mode 100644 index 0000000..c2594d5 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/NegConcatenationKernel.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_CONCATENATION_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_CONCATENATION_KERNEL_H + +#include "TestDataConcatenationBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace input_type_mismatch_concatenation +{ + +/* + * Concatenation Kernel with input1 type != input2 type: + * + * Input_1(1, 4, 4, 1)- Int32 Input_2(1, 4, 4, 2) - Float32 + * \ / + * Concatenation + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace input_type_mismatch_concatenation + +namespace concatenation_with_relu +{ + +/* + * Concatenation Kernel with relu activation: + * + * Input_1(1, 4, 4, 1)- Float32 Input_2(1, 4, 4, 2) - Float32 + * \ / + * Concatenation(ReLU) + * | + * Output(1, 4, 4, 3) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace concatenation_with_relu + +namespace concatenation_with_wrong_axis +{ + +/* + * Concatenation Kernel with wrong axis params: + * + * Input_1(1, 4, 4, 1)- Float32 Input_2(1, 4, 4, 2) - Float32 + * \ / + * Concatenation(axis = 6, should be < 4) + * | + * Output(1, 4, 4, 3) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace concatenation_with_wrong_axis + +class TestDataInputTypeMismatchConcatenation : public NegTestDataBase +{ +public: + TestDataInputTypeMismatchConcatenation() + { + _test_kernel_model_circle = input_type_mismatch_concatenation::test_kernel_model_circle; + } + + ~TestDataInputTypeMismatchConcatenation() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class TestDataReluConcatenation : public NegTestDataBase +{ +public: + TestDataReluConcatenation() + { + _test_kernel_model_circle = concatenation_with_relu::test_kernel_model_circle; + } + + ~TestDataReluConcatenation() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class TestDataWrongAxisConcatenation : public NegTestDataBase +{ +public: + TestDataWrongAxisConcatenation() + { + _test_kernel_model_circle = concatenation_with_wrong_axis::test_kernel_model_circle; + } + + ~TestDataWrongAxisConcatenation() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_CONCATENATION_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/TestDataConcatenationBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/TestDataConcatenationBase.h new file mode 100644 index 0000000..5b94eb0 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/concatenation/TestDataConcatenationBase.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataConcatenationBase : public TestDataBase +{ +public: + TestDataConcatenationBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONCATENATION_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/FloatConv2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/FloatConv2DKernel.h new file mode 100644 index 0000000..eef549c --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/FloatConv2DKernel.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_FLOAT_H + +#include "TestDataConv2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace conv2d_float +{ + +/* + * Conv2D Kernel: + * + * Input(1, 4, 3, 2) Weight(1, 2, 2, 2) Bias(2) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 2, 2, 2) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0xa0, 0xc0, + 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x80, 0x40, + 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0xc1, + 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xe0, 0x40, 0x00, 0x00, 0xa0, 0x40, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x84, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x72, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 18.776451, 25.97969, -9.277071, -3.5493946, 12.334248, 5.50226, -2.224743, -7.2292213, + 10.259663, -1.0846977, 15.823856, 3.3193378, 4.9413986, 4.3529205, -10.353054, 3.7166824, + 27.324902, -6.2231064, 10.370632, 22.661959, 20.206001, 8.245907, 9.984943, 21.379955}; + +const std::vector reference_output_data = {1.0177879, 128.43202, 0.0, 55.28556, + 39.483513, 0.0, 0.0, 7.0231743}; + +} // namespace conv2d_float + +class TestDataFloatConv2D : public TestDataConv2DBase +{ +public: + TestDataFloatConv2D() + { + _input_data = conv2d_float::input_data; + _reference_output_data = conv2d_float::reference_output_data; + _test_kernel_model_circle = conv2d_float::test_kernel_model_circle; + } + + ~TestDataFloatConv2D() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/NegConv2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/NegConv2DKernel.h new file mode 100644 index 0000000..52baf89 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/NegConv2DKernel.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_CONV_2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_CONV_2D_KERNEL_H + +#include "TestDataConv2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_conv2d_input_type_mismatch +{ + +/* + * Conv2D Kernel with input type mismatch (input_type should be equal to weight_type): + * + * Input(1, 3, 3, 2) - Float32 Weight(1, 1, 1, 2) - Int32 Bias(1) + * \ | / + * \ | / + * Conv2D + * | + * Output(1, 3, 3, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0xfe, 0x4c, 0xbf, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x74, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x72, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_conv2d_input_type_mismatch + +namespace neg_conv2d_bias_wrong_type +{ +/* + * Conv2D Kernel with wrong bias type (should be equal to input_type): + * + * Input(1, 3, 3, 2) - Float32 Weight(1, 1, 1, 2) - Float32 Bias(1) - Int32 + * \ | / + * \ | / + * Conv2D + * | + * Output(1, 3, 3, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x98, 0xb5, 0x03, 0xbe, 0x6b, 0x02, 0xeb, 0x3e, + 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x74, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x72, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_conv2d_bias_wrong_type + +namespace neg_conv2d_invalid_input_shape +{ +/* + * Conv2D Kernel with invalid input shape (rank should be == 4): + * + * Input(1, 1, 3, 3, 2) - Float32 Weight(1, 1, 1, 2) - Float32 Bias(1) - Int32 + * \ | / + * \ | / + * Conv2D + * | + * Output(1, 1, 3, 3, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xcb, 0xe5, 0x9c, 0x3f, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5b, 0xe7, 0x8c, 0xbf, 0x06, 0xc5, 0x2f, 0x3f, + 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x72, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_conv2d_invalid_input_shape + +class NegTestDataInputMismatchConv2DKernel : public NegTestDataBase +{ +public: + NegTestDataInputMismatchConv2DKernel() + { + _test_kernel_model_circle = neg_conv2d_input_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputMismatchConv2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongBiasTypeConv2DKernel : public NegTestDataBase +{ +public: + NegTestDataWrongBiasTypeConv2DKernel() + { + _test_kernel_model_circle = neg_conv2d_bias_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataWrongBiasTypeConv2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidInputTypeConv2DKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputTypeConv2DKernel() + { + _test_kernel_model_circle = neg_conv2d_bias_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputTypeConv2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_CONV_2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/TestDataConv2DBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/TestDataConv2DBase.h new file mode 100644 index 0000000..ce888ac --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/TestDataConv2DBase.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataConv2DBase : public TestDataBase +{ +public: + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/U8Conv2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/U8Conv2DKernel.h new file mode 100644 index 0000000..dd992be --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/conv2d/U8Conv2DKernel.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_U8_H +#define LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_U8_H + +#include "TestDataConv2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace conv2d_u8 +{ + +/* + * Conv2D Kernel: + * + * Input(1, 3, 3, 2) Weight(1, 1, 1, 2) Bias(1) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 3, 3, 1) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x00, 0xfc, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8d, 0x44, 0x00, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5c, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xc2, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0xb4, 0xfe, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xa1, 0xa0, 0x20, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x32, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x44, 0x00, 0x00, 0x00, 0x24, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x87, 0x7b, 0x24, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa1, 0xa0, 0x20, 0x3d, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, + 0x03, 0x00, 0x00, 0x00, 0x6b, 0x65, 0x72, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6f, 0x12, 0x83, 0x3b, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {5, 11, 5, 251, 5, 252, 22, 14, 251, + 5, 23, 5, 6, 245, 5, 33, 15, 252}; + +const std::vector reference_output_data = {103, 70, 70, 105, 142, 106, 71, 100, 71}; + +} // namespace conv2d_u8 + +class TestDataU8Conv2D : public TestDataConv2DBase +{ +public: + TestDataU8Conv2D() + { + _input_data = conv2d_u8::input_data; + _reference_output_data = conv2d_u8::reference_output_data; + _test_kernel_model_circle = conv2d_u8::test_kernel_model_circle; + } + + ~TestDataU8Conv2D() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_CONV_2D_KERNEL_U8_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/FloatDivKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/FloatDivKernel.h new file mode 100644 index 0000000..ed9408a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/FloatDivKernel.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_FLOAT_H + +#include "TestDataDivBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace div_float_with_broadcasting +{ + +/* + * Div Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Div(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {8.432024, 5.4664106, 16.856224, -10.004156, -14.128681, + 12.695552, -7.5779333, -1.1460792, 15.574873, -12.670321}; +const std::vector input2_data = {-2.0361109, -9.528288}; +const std::vector reference_output_data = {-4.14124, -2.6847312, -8.278638, 4.913365, + 6.939053, -1.3324064, 0.795309, 0.120281756, + -1.634593, 1.3297584}; + +} // namespace div_float_with_broadcasting + +namespace div_float_no_broadcasting +{ +/* + * Div Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Div(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {3.563036, 13.645134, 0.427146, 11.032923, 0.4189046, + 15.737275, 7.7726707, 0.75059056, -7.833488, 3.0679407}; +std::vector input2_data = {-0.62832826, 7.937863, -14.899745, 0.2819096, -5.8306913, + 8.6010685, -10.391579, -3.312385, -11.495937, 5.5657125}; +std::vector reference_output_data = {-5.67066, 1.7189934, -0.028668007, 39.136383, + -0.07184476, 1.8296884, -0.74797785, -0.22660124, + 0.6814136, 0.55122155}; + +} // namespace div_float_no_broadcasting + +class TestDataFloatDiv : public TestDataDivBase +{ +public: + explicit TestDataFloatDiv(bool is_with_broadcast) : TestDataDivBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = div_float_with_broadcasting::input1_data; + _input2_data = div_float_with_broadcasting::input2_data; + _reference_output_data = div_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = div_float_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = div_float_no_broadcasting::input1_data; + _input2_data = div_float_no_broadcasting::input2_data; + _reference_output_data = div_float_no_broadcasting::reference_output_data; + _test_kernel_model_circle = div_float_no_broadcasting::test_kernel_model_circle; + } + } + + ~TestDataFloatDiv() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/NegDivKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/NegDivKernel.h new file mode 100644 index 0000000..df42f95 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/NegDivKernel.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_DIV_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_DIV_KERNEL_H + +#include "TestDataDivBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace input_1_wrong_type +{ + +/* + * Div Kernel with input type mismatch: + * + * Input_1(2, 5) - Int32 Input_2(2, 1) - Float + * \ / + * Div(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace input_1_wrong_type + +namespace input_2_wrong_type +{ + +/* + * DIV Kernel with input type mismatch: + * + * Input_1(2, 5)- Float Input_2(2, 1) - Int32 + * \ / + * Div(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace input_2_wrong_type + +class NegTestDataInput1WrongTypeDiv : public NegTestDataBase +{ +public: + NegTestDataInput1WrongTypeDiv() + { + _test_kernel_model_circle = input_1_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataInput1WrongTypeDiv() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInput2WrongTypeDiv : public NegTestDataBase +{ +public: + NegTestDataInput2WrongTypeDiv() + { + _test_kernel_model_circle = input_2_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataInput2WrongTypeDiv() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_DIV_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/TestDataDivBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/TestDataDivBase.h new file mode 100644 index 0000000..e4894e1 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/div/TestDataDivBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataDivBase : public TestDataBase +{ +public: + explicit TestDataDivBase(bool) + { + // Do nothing + } + + TestDataDivBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_DIV_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/FloatEluKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/FloatEluKernel.h new file mode 100644 index 0000000..688933c --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/FloatEluKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_ELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_ELU_KERNEL_H + +#include "TestDataEluBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace elu_float +{ +/* + * Elu Kernel: + * + * Input(1, 3, 3, 2) + * | + * Elu + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-10.526339, -13.810211, -15.052563, -8.425814, 3.6157331, + 6.429944, -11.151951, 7.7377386, -2.4822063, 0.17121133, + 3.6448252, -6.6318836, 1.5882887, -4.6597095, 12.6291065, + -3.8438618, -2.5688074, -4.7900896}; + +const std::vector reference_output_data = { + -0.9999732, -0.999999, -0.9999997, -0.9997809, 3.6157331, 6.429944, + -0.99998564, 7.7377386, -0.9164414, 0.17121133, 3.6448252, -0.9986823, + 1.5882887, -0.9905308, 12.6291065, -0.97858924, -0.9233731, -0.9916883}; + +} // namespace elu_float + +class TestDataFloatElu : public TestDataEluBase +{ +public: + TestDataFloatElu() + { + _input_data = elu_float::input_data; + _reference_output_data = elu_float::reference_output_data; + _test_kernel_model_circle = elu_float::test_kernel_model_circle; + } + + ~TestDataFloatElu() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_ELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/NegEluKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/NegEluKernel.h new file mode 100644 index 0000000..db7ad33 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/NegEluKernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_ELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_ELU_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * Elu Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * Elu + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchEluKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchEluKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchEluKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_ELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/TestDataEluBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/TestDataEluBase.h new file mode 100644 index 0000000..5042237 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/elu/TestDataEluBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_ELU_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_ELU_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataEluBase : public TestDataBase +{ +public: + TestDataEluBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_ELU_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/FloatEqualKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/FloatEqualKernel.h new file mode 100644 index 0000000..1fce14f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/FloatEqualKernel.h @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_FLOAT_H + +#include "TestDataEqualBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_equal_float_with_no_broadcasting +{ + +/* + * Equal Kernel with input type mismatch + * + * Input_1(1, 4, 4, 3)-Float32 Input_2(1, 4, 4, 3)-Int32 + * \ / + * Equal(no broadcast) + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_equal_float_with_no_broadcasting + +namespace equal_float_with_no_broadcasting +{ + +/* + * Equal Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Equal(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {0.0, 33.11, -1.1, 0.0, 5.5, -2.0, 0.0, -1.0, -4.5, 1.1}; + +const std::vector input2_data = {0.0, 33.11, 1.2, 0.0, 5.5, -2.0, 0.01, -1.0, -4.5, 1.12}; + +const std::vector reference_output_data = {true, true, false, true, true, + true, false, true, true, false}; + +} // namespace equal_float_with_no_broadcasting + +namespace equal_float_with_broadcasting +{ + +/* + * Equal Kernel: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Equal(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-0.0, 0.0, -0.0, 0.0, 0.0, -0.0, 0.0, -0.0, -0.0, 0.0}; + +const std::vector input2_data = {0.0, -0.0, -0.0, 0.0, -0.0}; + +const std::vector reference_output_data = {true, true, true, true, true, + true, true, true, true, true}; + +} // namespace equal_float_with_broadcasting + +namespace neg_equal_float_with_broadcasting +{ + +/* + * Equal Kernel with input type mismatch: + * + * Input_1(2, 5)-float Input_2(1, 5)-Int32 + * \ / + * Equal(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_equal_float_with_broadcasting + +class TestDataFloatEqual : public TestDataEqualBase +{ +public: + explicit TestDataFloatEqual(bool is_with_broadcast, bool is_neg) + : TestDataEqualBase(is_with_broadcast) + { + if (not is_with_broadcast) + { + if (is_neg) + { + _input1_data = neg_equal_float_with_no_broadcasting::input1_data; + _input2_data = neg_equal_float_with_no_broadcasting::input2_data; + _reference_output_data = neg_equal_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_equal_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = equal_float_with_no_broadcasting::input1_data; + _input2_data = equal_float_with_no_broadcasting::input2_data; + _reference_output_data = equal_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = equal_float_with_no_broadcasting::test_kernel_model_circle; + } + } + else + { + if (is_neg) + { + _input1_data = neg_equal_float_with_broadcasting::input1_data; + _input2_data = neg_equal_float_with_broadcasting::input2_data; + _reference_output_data = neg_equal_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_equal_float_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = equal_float_with_broadcasting::input1_data; + _input2_data = equal_float_with_broadcasting::input2_data; + _reference_output_data = equal_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = equal_float_with_broadcasting::test_kernel_model_circle; + } + } + } + + ~TestDataFloatEqual() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/IntEqualKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/IntEqualKernel.h new file mode 100644 index 0000000..964ad6d --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/IntEqualKernel.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_INT_H + +#include "TestDataEqualBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace equal_int_with_no_broadcasting +{ + +/* + * Equal Kernel: + * + * Input_1(1, 5) Input_2(1, 5) + * \ / + * Equal(no broadcast) + * | + * Output(1, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {22, 31, 14, 5, 3}; + +const std::vector input2_data = {3, 14, 14, 5, 5}; + +const std::vector reference_output_data = {false, false, true, true, false}; + +} // namespace equal_int_with_no_broadcasting + +namespace neg_equal_int_with_no_broadcasting +{ + +/* + * Equal Kernel with input type mismatch: + * + * Input_1(1, 5)-int Input_2(1, 5)-float + * \ / + * Equal(no broadcast) + * | + * Output(1, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_equal_int_with_no_broadcasting + +namespace equal_int_with_broadcasting +{ +/* + * Equal Kernel: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Equal(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {12, 0, 0, -12, 0, 0, 12, -12, 0, -12}; + +const std::vector input2_data = {0, 12, 0, 0, 0}; + +const std::vector reference_output_data = {false, false, true, false, true, + true, true, false, true, false}; + +} // namespace equal_int_with_broadcasting + +namespace neg_equal_int_with_broadcasting +{ + +/* + * Equal Kernel with input type mismatch: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Equal(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_equal_int_with_broadcasting + +class TestDataIntEqual : public TestDataEqualBase +{ +public: + explicit TestDataIntEqual(bool is_with_broadcast, bool is_neg) + : TestDataEqualBase(is_with_broadcast) + { + if (is_with_broadcast) + { + if (is_neg) + { + _input1_data = neg_equal_int_with_broadcasting::input1_data; + _input2_data = neg_equal_int_with_broadcasting::input2_data; + _reference_output_data = neg_equal_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_equal_int_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = equal_int_with_broadcasting::input1_data; + _input2_data = equal_int_with_broadcasting::input2_data; + _reference_output_data = equal_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = equal_int_with_broadcasting::test_kernel_model_circle; + } + } + else + { + if (is_neg) + { + _input1_data = neg_equal_int_with_no_broadcasting::input1_data; + _input2_data = neg_equal_int_with_no_broadcasting::input2_data; + _reference_output_data = neg_equal_int_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_equal_int_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = equal_int_with_no_broadcasting::input1_data; + _input2_data = equal_int_with_no_broadcasting::input2_data; + _reference_output_data = equal_int_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = equal_int_with_no_broadcasting::test_kernel_model_circle; + } + } + } + + ~TestDataIntEqual() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/TestDataEqualBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/TestDataEqualBase.h new file mode 100644 index 0000000..c790f27 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/equal/TestDataEqualBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataEqualBase : public TestDataBase +{ +public: + explicit TestDataEqualBase(bool) + { + // Do nothing + } + + TestDataEqualBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_EQUAL_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/FloatExpKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/FloatExpKernel.h new file mode 100644 index 0000000..25370b3 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/FloatExpKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_EXP_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_EXP_KERNEL_H + +#include "TestDataExpBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace exp_float +{ +/* + * Exp Kernel: + * + * Input(1, 3, 3, 2) + * | + * Exp + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-18.223001, 5.126108, -14.195034, 14.899473, -5.9829874, + 4.536052, 6.039304, 6.410294, -7.008984, -16.432007, + 15.697407, -1.1735272, -2.2951646, -1.262989, 7.4088907, + 0.24651751, -10.7505045, 4.2837596}; +const std::vector reference_output_data = { + 1.2185715e-08, 168.36061, 6.8418734e-07, 2956371.5, 0.002521283, 93.32166, + 419.6008, 608.07245, 0.0009037262, 7.305839e-08, 6565943.5, 0.30927414, + 0.10074481, 0.28280744, 1650.5944, 1.2795616, 2.1434593e-05, 72.51254}; + +} // namespace exp_float + +class TestDataFloatExp : public TestDataExpBase +{ +public: + TestDataFloatExp() + { + _input_data = exp_float::input_data; + _reference_output_data = exp_float::reference_output_data; + _test_kernel_model_circle = exp_float::test_kernel_model_circle; + } + + ~TestDataFloatExp() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_EXP_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/NegExpKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/NegExpKernel.h new file mode 100644 index 0000000..ed31853 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/NegExpKernel.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_EXP_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_EXP_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * Exp Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * Exp + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchExpKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchExpKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchExpKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_EXP_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/TestDataExpBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/TestDataExpBase.h new file mode 100644 index 0000000..6427096 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/exp/TestDataExpBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_EXP_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_EXP_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataExpBase : public TestDataBase +{ +public: + TestDataExpBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_EXP_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/expand_dims/ExpandDimsKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/expand_dims/ExpandDimsKernel.h new file mode 100644 index 0000000..010d972 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/expand_dims/ExpandDimsKernel.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_EXPAND_DIMS_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_EXPAND_DIMS_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_test_expand_dims +{ + +/* + * ExpandDims Kernel: + * + * Input(3, 3) Const([1]) - wrong type for const + * | / + * ExpandDims + * | + * Output(3, 1, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_test_expand_dims + +namespace expand_dims_kernel +{ +/* + * ExpandDims Kernel: + * + * Input(3, 3) Const([1]) + * | / + * ExpandDims + * | + * Output(3, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {10.438837, -1.168417, -6.455261, -1.3638954, 31.58745, + 29.395872, -10.366383, 7.6131954, 9.837751}; + +const std::vector reference_output_data = {10.438837, -1.168417, -6.455261, + -1.3638954, 31.58745, 29.395872, + -10.366383, 7.6131954, 9.837751}; + +} // namespace expand_dims_kernel + +template class TestDataExpandDimsKernel : public TestDataBase +{ +public: + TestDataExpandDimsKernel() + { + _input_data = expand_dims_kernel::input_data; + _reference_output_data = expand_dims_kernel::reference_output_data; + _test_kernel_model_circle = expand_dims_kernel::test_kernel_model_circle; + } + + ~TestDataExpandDimsKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidInputTypeExpandDimsKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputTypeExpandDimsKernel() + { + _test_kernel_model_circle = neg_test_expand_dims::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputTypeExpandDimsKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_EXPAND_DIMS_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/FillKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/FillKernel.h new file mode 100644 index 0000000..e567361 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/FillKernel.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FILL_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FILL_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace fill_kernel +{ +/* + * Fill Kernel: + * + * Dims(3, 2) Input(scalar) + * \ / + * Fill + * | + * | + * Output(3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x64, 0x69, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5e, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {1.1f}; + +const std::vector reference_output_data = {1.1f, 1.1f, 1.1f, 1.1f, 1.1f, 1.1f}; +} // namespace fill_kernel + +template class TestDataFillKernel : public TestDataBase +{ +public: + TestDataFillKernel() + { + _input_data = fill_kernel::input_data; + _reference_output_data = fill_kernel::reference_output_data; + _test_kernel_model_circle = fill_kernel::test_kernel_model_circle; + } + + ~TestDataFillKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FILL_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/NegFillKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/NegFillKernel.h new file mode 100644 index 0000000..85f0e70 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fill/NegFillKernel.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_FILL_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_FILL_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_fill_kernel +{ +/* + * Fill Kernel with input output type mismatch (should be equal): + * + * Dims(3, 2) -Float32 Input(scalar) - Float32 + * \ / + * Fill + * | + * | + * Output(3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x64, 0x69, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_fill_kernel + +namespace neg_wrong_input_shape_fill_kernel +{ +/* + * Fill Kernel with input wrong shape (should be scalar): + * + * Dims(3, 2) -Float32 Input(5) - Float32 + * \ / + * Fill + * | + * | + * Output(3, 2) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x69, 0x6d, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_wrong_input_shape_fill_kernel + +class NegTestDataInputTypeMismatchFillKernel : public NegTestDataBase +{ +public: + NegTestDataInputTypeMismatchFillKernel() + { + _test_kernel_model_circle = + neg_input_output_type_mismatch_fill_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputTypeMismatchFillKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongInputShapeFillKernel : public NegTestDataBase +{ +public: + NegTestDataWrongInputShapeFillKernel() + { + _test_kernel_model_circle = neg_wrong_input_shape_fill_kernel::test_kernel_model_circle; + } + + ~NegTestDataWrongInputShapeFillKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_FILL_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/FloatFullyConnectedKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/FloatFullyConnectedKernel.h new file mode 100644 index 0000000..55ee09f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/FloatFullyConnectedKernel.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_FLOAT_H + +#include "TestDataFullyConnectedBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace fully_connected_float +{ + +/* + * FullyConnected Kernel: + * + * Input(1, 16) Weight(4, 16) Bias(4) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 4) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x4c, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x80, 0x40, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xc0, + 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0xa0, 0xc0, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0xc0, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xe0, 0x40, + 0x00, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xc0, + 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0xa0, 0xc0, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0xc0, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xe0, 0x40, + 0x00, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xc0, + 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0xa0, 0xc0, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0xc0, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xe0, 0x40, + 0x00, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xc0, + 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0xa0, 0xc0, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xe0, 0xc0, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x80, 0xbf, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xe0, 0x40, + 0x00, 0x00, 0xa0, 0x40, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 17.491695, 15.660671, 4.7347794, -15.796822, 20.4776, 18.438372, -0.7529831, 10.671711, + 10.699566, 3.1682281, -22.776001, 1.527811, -0.1198349, -5.748741, -5.1772327, 20.06879}; + +const std::vector reference_output_data = {263.84323, 260.84323, 259.84323, 266.84323}; + +} // namespace fully_connected_float + +class TestDataFloatFullyConnected : public TestDataFullyConnectedBase +{ +public: + TestDataFloatFullyConnected() + { + _input_data = fully_connected_float::input_data; + _reference_output_data = fully_connected_float::reference_output_data; + _test_kernel_model_circle = fully_connected_float::test_kernel_model_circle; + } + + ~TestDataFloatFullyConnected() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/NegFullyConnectedKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/NegFullyConnectedKernel.h new file mode 100644 index 0000000..785c4ce --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/NegFullyConnectedKernel.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_FULLY_CONNECTED_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_FULLY_CONNECTED_KERNEL_H + +#include "TestDataFullyConnectedBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_fully_connected_wrong_weight_type +{ +/* + * FullyConnected Kernel with wrong weight type (Int16): + * + * Input(1, 4) Weight(4, 4) - Int16 Bias(4) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 4) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_fully_connected_wrong_weight_type + +namespace neg_fully_connected_wrong_weight_shape +{ +/* + * FullyConnected Kernel with wrong weight shape (rank should be 2): + * + * Input(1, 64) Weight(1, 8, 64) Bias(8) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 8) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xb0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_fully_connected_wrong_weight_shape + +namespace neg_fully_connected_wrong_bias_shape +{ +/* + * FullyConnected Kernel with wrong bias shape should be equal to output.dim(1): + * + * Input(1, 64) Weight(1, 8, 64) Bias(15) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 8) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_fully_connected_wrong_bias_shape + +class NegTestDataWrongWeightTypeFullyConnectedKernel : public NegTestDataBase +{ +public: + NegTestDataWrongWeightTypeFullyConnectedKernel() + { + _test_kernel_model_circle = neg_fully_connected_wrong_weight_type::test_kernel_model_circle; + } + + ~NegTestDataWrongWeightTypeFullyConnectedKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongWeightShapeFullyConnectedKernel : public NegTestDataBase +{ +public: + NegTestDataWrongWeightShapeFullyConnectedKernel() + { + _test_kernel_model_circle = neg_fully_connected_wrong_weight_shape::test_kernel_model_circle; + } + + ~NegTestDataWrongWeightShapeFullyConnectedKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongBiasShapeFullyConnectedKernel : public NegTestDataBase +{ +public: + NegTestDataWrongBiasShapeFullyConnectedKernel() + { + _test_kernel_model_circle = neg_fully_connected_wrong_bias_shape::test_kernel_model_circle; + } + + ~NegTestDataWrongBiasShapeFullyConnectedKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_FULLY_CONNECTED_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/TestDataFullyConnectedBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/TestDataFullyConnectedBase.h new file mode 100644 index 0000000..582b3db --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/TestDataFullyConnectedBase.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataFullyConnectedBase : public TestDataBase +{ +public: + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/U8FullyConnectedKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/U8FullyConnectedKernel.h new file mode 100644 index 0000000..f5d9706 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/fully_connected/U8FullyConnectedKernel.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_U8_H +#define LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_U8_H + +#include "TestDataFullyConnectedBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace fully_connected_u8 +{ + +/* + * FullyConnected Kernel: + * + * Input(1, 4) Weight(4, 4) Bias(4) + * \ | / + * \ | / + * FullyConnected + * | + * Output(1, 4) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0xbc, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x16, 0x04, 0x2d, 0x17, + 0x0b, 0x2b, 0x05, 0x2c, 0x06, 0x2c, 0x4e, 0x6f, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xfa, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x99, 0x96, 0xfd, 0x3b, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, + 0x84, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf0, 0xf1, 0xae, 0x3b, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x68, 0x68, 0x2b, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {5, 3, 251, 5}; + +const std::vector reference_output_data = {5, 10, 5, 13}; + +} // namespace fully_connected_u8 + +class TestDataU8FullyConnected : public TestDataFullyConnectedBase +{ +public: + TestDataU8FullyConnected() + { + _input_data = fully_connected_u8::input_data; + _reference_output_data = fully_connected_u8::reference_output_data; + _test_kernel_model_circle = fully_connected_u8::test_kernel_model_circle; + } + + ~TestDataU8FullyConnected() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FULLY_CONNECTED_KERNEL_U8_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/FloatGatherKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/FloatGatherKernel.h new file mode 100644 index 0000000..570ebbf --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/FloatGatherKernel.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_H + +#include "TestDataGatherBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace gather_float +{ +/* + * Gather Kernel: + * + * Input(1, 2, 3, 4) Indices(1, 2) + * \ / + * Gather(axis=2) + * | + * Output(1, 2, 2, 4) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -21.338402, 27.906258, 12.78171, 9.978121, 25.450222, -11.091215, -8.654621, 1.2170105, + -4.5460815, 19.334154, 6.8392344, 5.622982, 2.2990818, -8.733818, 8.312399, -2.8866997, + 13.171104, 3.4856339, -17.577343, 20.683546, 1.0197582, 27.619759, -6.016859, -4.398407}; + +const std::vector reference_output_data = { + 25.450222, -11.091215, -8.654621, 1.2170105, -4.5460815, 19.334154, 6.8392344, 5.622982, + 13.171104, 3.4856339, -17.577343, 20.683546, 1.0197582, 27.619759, -6.016859, -4.398407}; + +} // namespace gather_float + +class TestDataFloatGather : public TestDataGatherBase +{ +public: + TestDataFloatGather() + { + _input_data = gather_float::input_data; + _reference_output_data = gather_float::reference_output_data; + _test_kernel_model_circle = gather_float::test_kernel_model_circle; + } + + ~TestDataFloatGather() override = default; +}; +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/IntGatherKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/IntGatherKernel.h new file mode 100644 index 0000000..5c7edef --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/IntGatherKernel.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GATHER_INT_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_GATHER_INT_KERNEL_H + +#include "TestDataGatherBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace gather_int +{ +/* + * Gather Kernel: + * + * Input(1, 2, 3, 4) Indices(1, 2) + * \ / + * Gather(axis=2) + * | + * Output(1, 2, 2, 4) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {4, 14, 14, 22, 5, -5, -4, -3, 5, 15, 13, 5, + -3, 5, -13, 15, -6, -13, -4, -12, -5, 5, 5, -5}; + +const std::vector reference_output_data = {5, -5, -4, -3, 5, 15, 13, 5, + -6, -13, -4, -12, -5, 5, 5, -5}; + +} // namespace gather_int + +class TestDataIntGather : public TestDataGatherBase +{ +public: + TestDataIntGather() + { + _input_data = gather_int::input_data; + _reference_output_data = gather_int::reference_output_data; + _test_kernel_model_circle = gather_int::test_kernel_model_circle; + } + + ~TestDataIntGather() override = default; +}; +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GATHER_INT_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/NegGatherKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/NegGatherKernel.h new file mode 100644 index 0000000..a902b72 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/NegGatherKernel.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_GATHER_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_GATHER_KERNEL_H + +#include "TestDataGatherBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_gather_mismatch_input_output_type +{ +/* + * Gather Kernel with input output type mismatch (should be equal): + * + * Input(1, 2, 3, 4) - S16 Indices(1, 2) + * \ / + * Gather(axis=2) + * | + * Output(1, 2, 2, 4) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_gather_mismatch_input_output_type + +namespace neg_gather_wrong_position_type +{ +/* + * Gather Kernel with wrong Indices type(should be S32): + * + * Input(1, 2, 3, 4) - S16 Indices(1, 2) - Float32 + * \ / + * Gather(axis=2) + * | + * Output(1, 2, 2, 4) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x00, 0x40, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_gather_wrong_position_type + +namespace neg_gather_wrong_axis +{ +/* + * Gather Kernel with wrong axis value(should be < rank(input)): + * + * Input(1, 2, 3, 4) - S16 Indices(1, 2) - Float32 + * \ / + * Gather(axis=10) + * | + * Output(1, 2, 2, 4) - Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_gather_wrong_axis + +class NegTestDataInputOutputTypeMismatchGatherKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchGatherKernel() + { + _test_kernel_model_circle = neg_gather_mismatch_input_output_type::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchGatherKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongPositionTypeGatherKernel : public NegTestDataBase +{ +public: + NegTestDataWrongPositionTypeGatherKernel() + { + _test_kernel_model_circle = neg_gather_wrong_position_type::test_kernel_model_circle; + } + + ~NegTestDataWrongPositionTypeGatherKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongAxisGatherKernel : public NegTestDataBase +{ +public: + NegTestDataWrongAxisGatherKernel() + { + _test_kernel_model_circle = neg_gather_wrong_axis::test_kernel_model_circle; + } + + ~NegTestDataWrongAxisGatherKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_GATHER_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/TestDataGatherBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/TestDataGatherBase.h new file mode 100644 index 0000000..e38fcb8 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/gather/TestDataGatherBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataGatherBase : public TestDataBase +{ +public: + TestDataGatherBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GATHER_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/FloatGreaterKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/FloatGreaterKernel.h new file mode 100644 index 0000000..dcb7b68 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/FloatGreaterKernel.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_FLOAT_H + +#include "TestDataGreaterBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace greater_float +{ + +/* + * Greater Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Greater(no broadcast) + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -0.01787583, -0.8314556, -0.47624078, -1.9747407, -0.51676583, -0.20183715, -1.9358647, + 0.7616414, -0.0899037, 2.048367, -1.3174965, 1.5267943, 0.68707687, 1.3464743, + 0.98674047, -1.4853697, 1.9973947, 0.5170953, 0.37471953, -1.6011852, 0.32045737, + -0.6598305, -1.7946662, 1.2349467, 1.3320708, 0.5151753, 1.345111, -0.16560331, + 0.82792366, -1.734876, 0.043626763, -0.0118546495, 0.31535238, 0.1888555, -0.32523626, + -0.997665, 0.5819472, -2.3194845, -1.6897905, 0.9981752, -1.2897044, 0.75768864, + 0.56781554, -1.0565805, -1.4891449, 0.2493645, -1.1312587, 0.6837854}; + +const std::vector input2_data = { + 0.30809638, -0.28285328, -0.8437058, 1.7689779, 0.5182942, 0.571205, -0.89484423, + 0.28100377, 0.5453497, 1.3848042, -0.04359268, -1.7448778, -0.5375435, -0.85059136, + -0.77961826, -0.4916915, 1.3359088, -0.09580261, 0.6158275, -0.05056348, 0.90505254, + 0.94226706, 1.136139, -0.45077038, -0.5018571, -1.1543767, 0.85094684, -0.13731039, + -0.3298641, 0.9474698, -0.48497504, -0.14864737, -0.009302358, -1.1259161, 0.44226727, + 1.0149708, 0.36024934, 0.4969523, 0.45014778, -0.34718898, 1.2260172, 0.35304692, + -1.3037513, -0.2565706, 0.18085766, -0.7099202, -0.9203537, -1.2257448}; + +const std::vector reference_output_data = { + false, false, true, false, false, false, false, true, false, true, false, true, + true, true, true, false, true, true, false, false, false, false, false, true, + true, true, true, false, true, false, true, true, true, true, false, false, + true, false, false, true, false, true, true, false, false, true, false, true}; + +} // namespace greater_float + +namespace neg_greater_float_with_no_broadcasting +{ + +/* + * Greater Kernel with input type mismatch: + * + * Input_1(1, 4, 4, 3)-float Input_2(1, 4, 4, 3)-int + * \ / + * Greater(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_greater_float_with_no_broadcasting + +class TestDataFloatGreater : public TestDataGreaterBase +{ +public: + explicit TestDataFloatGreater(bool is_with_broadcast, bool is_neg) + : TestDataGreaterBase(is_with_broadcast) + { + if (is_with_broadcast) + { + assert(false && "Not impl yet"); + } + else + { + if (is_neg) + { + _input1_data = neg_greater_float_with_no_broadcasting::input1_data; + _input2_data = neg_greater_float_with_no_broadcasting::input2_data; + _reference_output_data = neg_greater_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = + neg_greater_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = greater_float::input1_data; + _input2_data = greater_float::input2_data; + _reference_output_data = greater_float::reference_output_data; + _test_kernel_model_circle = greater_float::test_kernel_model_circle; + } + } + } + + ~TestDataFloatGreater() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/TestDataGreaterBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/TestDataGreaterBase.h new file mode 100644 index 0000000..7c2005a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater/TestDataGreaterBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataGreaterBase : public TestDataBase +{ +public: + explicit TestDataGreaterBase(bool) + { + // Do nothing + } + + TestDataGreaterBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/FloatGreaterEqualKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/FloatGreaterEqualKernel.h new file mode 100644 index 0000000..14226ab --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/FloatGreaterEqualKernel.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GREATER_EQUAL_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_GREATER_EQUAL_KERNEL_FLOAT_H + +#include "TestDataGreaterEqualBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace greater_equal_float +{ + +/* + * GreaterEqual Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * GreaterEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -0.01787583, -0.8314556, -0.47624078, -1.9747407, -0.51676583, -0.20183715, -1.9358647, + 0.7616414, -0.0899037, 2.048367, -1.3174965, 1.5267943, 0.68707687, 1.3464743, + 0.98674047, -1.4853697, 1.9973947, 0.5170953, 0.37471953, -1.6011852, 0.32045737, + -0.6598305, -1.7946662, 1.2349467, 1.3320708, 0.5151753, 1.345111, -0.16560331, + 0.82792366, -1.734876, 0.043626763, -0.0118546495, 0.31535238, 0.1888555, -0.32523626, + -0.997665, 0.5819472, -2.3194845, -1.6897905, 0.9981752, -1.2897044, 0.75768864, + 0.56781554, -1.0565805, -1.4891449, 0.2493645, -1.1312587, 0.6837854}; + +const std::vector input2_data = { + 0.30809638, -0.28285328, -0.8437058, 1.7689779, 0.5182942, 0.571205, -0.89484423, + 0.28100377, 0.5453497, 1.3848042, -0.04359268, -1.7448778, -0.5375435, -0.85059136, + -0.77961826, -0.4916915, 1.3359088, -0.09580261, 0.6158275, -0.05056348, 0.90505254, + 0.94226706, 1.136139, -0.45077038, -0.5018571, -1.1543767, 0.85094684, -0.13731039, + -0.3298641, 0.9474698, -0.48497504, -0.14864737, -0.009302358, -1.1259161, 0.44226727, + 1.0149708, 0.36024934, 0.4969523, 0.45014778, -0.34718898, 1.2260172, 0.35304692, + -1.3037513, -0.2565706, 0.18085766, -0.7099202, -0.9203537, -1.2257448}; + +const std::vector reference_output_data = { + false, false, true, false, false, false, false, true, false, true, false, true, + true, true, true, false, true, true, false, false, false, false, false, true, + true, true, true, false, true, false, true, true, true, true, false, false, + true, false, false, true, false, true, true, false, false, true, false, true}; + +} // namespace greater_equal_float + +namespace neg_greater_equal_float_with_no_broadcasting +{ + +/* + * GreaterEqual Kernel with input type mismatch: + * + * Input_1(1, 4, 4, 3)-float Input_2(1, 4, 4, 3)-int + * \ / + * GreaterEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + +}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_greater_equal_float_with_no_broadcasting + +class TestDataFloatGreaterEqual : public TestDataGreaterEqualBase +{ +public: + explicit TestDataFloatGreaterEqual(bool is_with_broadcast, bool is_neg) + : TestDataGreaterEqualBase(is_with_broadcast) + { + if (is_with_broadcast) + { + assert(false && "Not impl yet"); + } + else + { + if (is_neg) + { + _input1_data = neg_greater_equal_float_with_no_broadcasting::input1_data; + _input2_data = neg_greater_equal_float_with_no_broadcasting::input2_data; + _reference_output_data = + neg_greater_equal_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = + neg_greater_equal_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = greater_equal_float::input1_data; + _input2_data = greater_equal_float::input2_data; + _reference_output_data = greater_equal_float::reference_output_data; + _test_kernel_model_circle = greater_equal_float::test_kernel_model_circle; + } + } + } + + ~TestDataFloatGreaterEqual() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GREATER_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/TestDataGreaterEqualBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/TestDataGreaterEqualBase.h new file mode 100644 index 0000000..9153c35 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/greater_equal/TestDataGreaterEqualBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_GREATER_EQUAL_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_GREATER_EQUAL_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataGreaterEqualBase : public TestDataBase +{ +public: + explicit TestDataGreaterEqualBase(bool) + { + // Do nothing + } + + TestDataGreaterEqualBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_GREATER_EQUAL_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/FloatLeakyReLUKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/FloatLeakyReLUKernel.h new file mode 100644 index 0000000..6d1e81f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/FloatLeakyReLUKernel.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_LEAKY_RELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_LEAKY_RELU_KERNEL_H + +#include "TestDataLeakyReLUBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace leaky_relu_float +{ +/* + * Leaky_ReLU Kernel: + * + * Input(1, 3, 3, 2) + * | + * Leaky_ReLU + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x62, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-19.160503, 32.721092, 9.924562, -5.1152186, -4.792659, + 5.404521, 21.64431, 4.1435075, -9.379442, 10.986649, + -19.975468, -2.6520946, 9.306602, -12.589155, -2.9080758, + 21.732197, -2.6927, -2.0605793}; + +const std::vector reference_output_data = { + -38.321007, 32.721092, 9.924562, -10.230437, -9.585318, 5.404521, + 21.64431, 4.1435075, -18.758884, 10.986649, -39.950935, -5.304189, + 9.306602, -25.17831, -5.8161516, 21.732197, -5.3854, -4.1211586}; + +} // namespace leaky_relu_float + +class TestDataFloatLeakyReLU : public TestDataLeakyReLUBase +{ +public: + TestDataFloatLeakyReLU() + { + _input_data = leaky_relu_float::input_data; + _reference_output_data = leaky_relu_float::reference_output_data; + _test_kernel_model_circle = leaky_relu_float::test_kernel_model_circle; + } + + ~TestDataFloatLeakyReLU() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_LEAKY_RELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/NegLeakyReLUKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/NegLeakyReLUKernel.h new file mode 100644 index 0000000..1d6ebde --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/NegLeakyReLUKernel.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_LEAKY_RELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_LEAKY_RELU_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * LeakyReLU Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * LeakyReLU + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x62, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchLeakyReLUKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchLeakyReLUKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchLeakyReLUKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_LEAKY_RELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/TestDataLeakyReLUBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/TestDataLeakyReLUBase.h new file mode 100644 index 0000000..ed8346a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/leaky_relu/TestDataLeakyReLUBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LEAKY_RELU_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LEAKY_RELU_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLeakyReLUBase : public TestDataBase +{ +public: + TestDataLeakyReLUBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LEAKY_RELU_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/FloatLessKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/FloatLessKernel.h new file mode 100644 index 0000000..56556d1 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/FloatLessKernel.h @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_FLOAT_H + +#include "TestDataLessBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_less_float_with_no_broadcasting +{ + +/* + * Less Kernel with input type mismatch + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Less(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_float_with_no_broadcasting + +namespace less_float_with_no_broadcasting +{ + +/* + * Less Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Less(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -17.109156, -14.220402, 0.6967888, 8.5724945, -9.638723, -15.630436, 5.7072678, -28.719913, + 26.073956, 28.855093, -15.886295, -11.779518, 7.330929, -0.13710785, 15.124775, 9.482744, + 1.2159233, -11.10869, -7.9041195, 32.05063, 1.4171028, -9.373051, -3.7985916, -0.6265869, + 1.1357956, 1.3873901, -6.6756783, 7.348675, -3.1261578, 13.6670475, 13.453075, 2.7914486, + 24.654053, 23.756575, 3.0251846, -6.2888947, 15.202778, -6.0607758, 1.6703491, -18.499111, + 10.935091, 1.846302, -16.904373, 0.9744568, -1.1621361, -2.4073143, 4.1701775, 4.268633}; + +const std::vector input2_data = { + 14.16371, -9.996677, 23.359705, -5.8362885, 8.50577, 3.890133, 26.986832, 11.293919, + -7.2040367, -5.077221, -11.096642, 20.064266, 20.187872, -2.297474, 19.889074, -24.76117, + -9.60951, 25.72523, 22.055315, 20.373281, -4.083912, -1.6361217, -4.452694, 22.31394, + 1.7857666, -3.4938774, -0.95575714, -6.792, 24.483788, 14.758501, 8.167406, -13.673744, + 1.8645649, -5.4475937, 11.297581, 38.236015, -4.01342, 26.875057, 0.6700249, 39.450253, + -11.167023, 13.393299, -0.7329292, 10.980518, -3.8029938, -16.393318, 5.341381, -40.322437}; + +const std::vector reference_output_data = { + true, true, true, false, true, true, true, true, false, false, true, true, + true, false, true, false, false, true, true, false, false, true, false, true, + true, false, true, false, true, true, false, false, false, false, true, true, + false, true, false, true, false, true, true, true, false, false, true, false}; + +} // namespace less_float_with_no_broadcasting + +namespace less_float_with_broadcasting +{ + +/* + * Less Kernel: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x31, 0x34, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {11.259954, 0.61867523, -7.2976017, 16.326784, -1.7243233, + -9.790066, -2.8924255, -1.1683407, -7.3359947, 22.979622}; + +const std::vector input2_data = {0.67757416, 10.977215, 6.6511993, -7.3085804, 8.511749}; + +const std::vector reference_output_data = {false, true, true, false, true, + true, true, true, true, false}; + +} // namespace less_float_with_broadcasting + +namespace neg_less_float_with_broadcasting +{ + +/* + * Less Kernel with input type mismatch: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_float_with_broadcasting + +class TestDataFloatLess : public TestDataLessBase +{ +public: + explicit TestDataFloatLess(bool is_with_broadcast, bool is_neg) + : TestDataLessBase(is_with_broadcast) + { + if (not is_with_broadcast) + { + if (is_neg) + { + _input1_data = neg_less_float_with_no_broadcasting::input1_data; + _input2_data = neg_less_float_with_no_broadcasting::input2_data; + _reference_output_data = neg_less_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_less_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_float_with_no_broadcasting::input1_data; + _input2_data = less_float_with_no_broadcasting::input2_data; + _reference_output_data = less_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = less_float_with_no_broadcasting::test_kernel_model_circle; + } + } + else + { + if (is_neg) + { + _input1_data = neg_less_float_with_broadcasting::input1_data; + _input2_data = neg_less_float_with_broadcasting::input2_data; + _reference_output_data = neg_less_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_less_float_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_float_with_broadcasting::input1_data; + _input2_data = less_float_with_broadcasting::input2_data; + _reference_output_data = less_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = less_float_with_broadcasting::test_kernel_model_circle; + } + } + } + + ~TestDataFloatLess() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/IntLessKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/IntLessKernel.h new file mode 100644 index 0000000..750d45d --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/IntLessKernel.h @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_INT_H + +#include "TestDataLessBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace less_int_with_no_broadcasting +{ + +/* + * Less Kernel: + * + * Input_1(1, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(1, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x31, 0x34, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {22, 31, 14, 5, 3}; + +const std::vector input2_data = {3, 14, 5, 14, 5}; + +const std::vector reference_output_data = {false, false, false, true, true}; + +} // namespace less_int_with_no_broadcasting + +namespace neg_less_int_with_no_broadcasting +{ + +/* + * Less Kernel with input type mismatch: + * + * Input_1(1, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(1, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_int_with_no_broadcasting + +namespace less_int_with_broadcasting +{ + +/* + * Less Kernel: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x31, 0x34, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x14, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-4, 15, 4, 21, -3, 13, 5, -3, -5, 5}; + +const std::vector input2_data = {5, 5, 6, 5, 6}; + +const std::vector reference_output_data = {true, false, true, false, true, + false, false, true, true, true}; + +} // namespace less_int_with_broadcasting + +namespace neg_less_int_with_broadcasting +{ + +/* + * Less Kernel with input type mismatch: + * + * Input_1(2, 5) Input_2(1, 5) + * \ / + * Less(with broadcast) + * | + * Output(2, 5) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_int_with_broadcasting + +class TestDataIntLess : public TestDataLessBase +{ +public: + explicit TestDataIntLess(bool is_with_broadcast, bool is_neg) + : TestDataLessBase(is_with_broadcast) + { + if (is_with_broadcast) + { + if (is_neg) + { + _input1_data = neg_less_int_with_broadcasting::input1_data; + _input2_data = neg_less_int_with_broadcasting::input2_data; + _reference_output_data = neg_less_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_less_int_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_int_with_broadcasting::input1_data; + _input2_data = less_int_with_broadcasting::input2_data; + _reference_output_data = less_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = less_int_with_broadcasting::test_kernel_model_circle; + } + } + else + { + if (is_neg) + { + _input1_data = neg_less_int_with_no_broadcasting::input1_data; + _input2_data = neg_less_int_with_no_broadcasting::input2_data; + _reference_output_data = neg_less_int_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_less_int_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_int_with_no_broadcasting::input1_data; + _input2_data = less_int_with_no_broadcasting::input2_data; + _reference_output_data = less_int_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = less_int_with_no_broadcasting::test_kernel_model_circle; + } + } + } + + ~TestDataIntLess() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/NegTestDataLessKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/NegTestDataLessKernel.h new file mode 100644 index 0000000..c81212a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/NegTestDataLessKernel.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_NEG_TEST_MODELS_LESS_KERNEL_H +#define LUCI_INTERPRETER_NEG_TEST_MODELS_LESS_KERNEL_H + +#include "TestDataLessBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_less_kernel_wrong_output +{ + +/* + * Less Kernel with wrong output type + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Less(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_less_kernel_wrong_output + +class NegTestDataLessKernel : public NegTestDataBase +{ +public: + NegTestDataLessKernel() + { + _test_kernel_model_circle = neg_less_kernel_wrong_output::test_kernel_model_circle; + } + + ~NegTestDataLessKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_NEG_TEST_MODELS_LESS_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/QuantLessKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/QuantLessKernel.h new file mode 100644 index 0000000..df51e05 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/QuantLessKernel.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_QUANT_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_QUANT_H + +#include "TestDataLessBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace less_uint8_with_no_broadcasting +{ + +/* + * Less Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Less(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, + 0x7c, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x81, 0x80, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x81, 0x80, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + 14, 5, 251, 243, 14, 243, 251, 5, 34, 252, 245, 252, 11, 250, 31, 14, + 5, 250, 21, 5, 24, 233, 5, 235, 23, 25, 15, 6, 22, 251, 23, 252, + 242, 5, 14, 21, 234, 242, 5, 253, 16, 244, 5, 13, 21, 5, 15, 252}; + +const std::vector input2_data = {5, 2, 5, 14, 14, 24, 15, 5, 13, 4, 5, 251, + 241, 14, 253, 253, 5, 254, 245, 5, 13, 40, 253, 5, + 253, 235, 5, 5, 252, 252, 5, 5, 5, 5, 252, 253, + 252, 13, 251, 251, 7, 253, 13, 5, 5, 253, 5, 5}; + +const std::vector reference_output_data = { + false, false, false, false, false, false, false, false, false, false, false, false, + true, false, true, true, false, true, true, false, false, false, true, false, + true, true, false, false, true, true, false, false, false, false, true, true, + true, false, true, false, false, true, true, false, false, true, false, false}; + +} // namespace less_uint8_with_no_broadcasting + +namespace neg_less_uint8_with_no_broadcasting +{ + +/* + * Less Kernel with input type mismatch: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * Less(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = {}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_uint8_with_no_broadcasting + +class TestDataQuantLess : public TestDataLessBase +{ +public: + explicit TestDataQuantLess(bool is_with_broadcast, bool is_neg) + : TestDataLessBase(is_with_broadcast) + { + if (is_with_broadcast) + { + assert(false && "Not impl yet"); + } + else + { + if (is_neg) + { + _input1_data = neg_less_uint8_with_no_broadcasting::input1_data; + _input2_data = neg_less_uint8_with_no_broadcasting::input2_data; + _reference_output_data = neg_less_uint8_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = neg_less_uint8_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_uint8_with_no_broadcasting::input1_data; + _input2_data = less_uint8_with_no_broadcasting::input2_data; + _reference_output_data = less_uint8_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = less_uint8_with_no_broadcasting::test_kernel_model_circle; + } + } + } + + ~TestDataQuantLess() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_QUANT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/TestDataLessBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/TestDataLessBase.h new file mode 100644 index 0000000..1264ab3 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less/TestDataLessBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLessBase : public TestDataBase +{ +public: + explicit TestDataLessBase(bool) + { + // Do nothing + } + + TestDataLessBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/FloatLessEqualKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/FloatLessEqualKernel.h new file mode 100644 index 0000000..c20966d --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/FloatLessEqualKernel.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_EQUAL_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_EQUAL_KERNEL_FLOAT_H + +#include "TestDataLessEqualBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace less_equal_float +{ + +/* + * LessEqual Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * LessEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -0.01787583, -0.8314556, -0.47624078, -1.9747407, -0.51676583, -0.20183715, -1.9358647, + 0.7616414, -0.0899037, 2.048367, -1.3174965, 1.5267943, 0.68707687, 1.3464743, + 0.98674047, -1.4853697, 1.9973947, 0.5170953, 0.37471953, -1.6011852, 0.32045737, + -0.6598305, -1.7946662, 1.2349467, 1.3320708, 0.5151753, 1.345111, -0.16560331, + 0.82792366, -1.734876, 0.043626763, -0.0118546495, 0.31535238, 0.1888555, -0.32523626, + -0.997665, 0.5819472, -2.3194845, -1.6897905, 0.9981752, -1.2897044, 0.75768864, + 0.56781554, -1.0565805, -1.4891449, 0.2493645, -1.1312587, 0.6837854}; + +const std::vector input2_data = { + 0.30809638, -0.28285328, -0.8437058, 1.7689779, 0.5182942, 0.571205, -0.89484423, + 0.28100377, 0.5453497, 1.3848042, -0.04359268, -1.7448778, -0.5375435, -0.85059136, + -0.77961826, -0.4916915, 1.3359088, -0.09580261, 0.6158275, -0.05056348, 0.90505254, + 0.94226706, 1.136139, -0.45077038, -0.5018571, -1.1543767, 0.85094684, -0.13731039, + -0.3298641, 0.9474698, -0.48497504, -0.14864737, -0.009302358, -1.1259161, 0.44226727, + 1.0149708, 0.36024934, 0.4969523, 0.45014778, -0.34718898, 1.2260172, 0.35304692, + -1.3037513, -0.2565706, 0.18085766, -0.7099202, -0.9203537, -1.2257448}; + +const std::vector reference_output_data = { + true, true, false, true, true, true, true, false, true, false, true, false, + false, false, false, true, false, false, true, true, true, true, true, false, + false, false, false, true, false, true, false, false, false, false, true, true, + false, true, true, false, true, false, false, true, true, false, true, false}; + +} // namespace less_equal_float + +namespace neg_less_equal_float_with_no_broadcasting +{ + +/* + * LessEqual Kernel with input type mismatch: + * + * Input_1(1, 4, 4, 3)-float Input_2(1, 4, 4, 3)-int + * \ / + * LessEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_less_equal_float_with_no_broadcasting + +class TestDataFloatLessEqual : public TestDataLessEqualBase +{ +public: + explicit TestDataFloatLessEqual(bool is_with_broadcast, bool is_neg) + : TestDataLessEqualBase(is_with_broadcast) + { + if (is_with_broadcast) + { + assert(false && "Not impl yet"); + } + else + { + if (is_neg) + { + _input1_data = neg_less_equal_float_with_no_broadcasting::input1_data; + _input2_data = neg_less_equal_float_with_no_broadcasting::input2_data; + _reference_output_data = neg_less_equal_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = + neg_less_equal_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = less_equal_float::input1_data; + _input2_data = less_equal_float::input2_data; + _reference_output_data = less_equal_float::reference_output_data; + _test_kernel_model_circle = less_equal_float::test_kernel_model_circle; + } + } + } + + ~TestDataFloatLessEqual() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/TestDataLessEqualBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/TestDataLessEqualBase.h new file mode 100644 index 0000000..4607737 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/less_equal/TestDataLessEqualBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LESS_EQUAL_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LESS_EQUAL_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLessEqualBase : public TestDataBase +{ +public: + explicit TestDataLessEqualBase(bool) + { + // Do nothing + } + + TestDataLessEqualBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LESS_EQUAL_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/BoolLogicalAndKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/BoolLogicalAndKernel.h new file mode 100644 index 0000000..8b87fa2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/BoolLogicalAndKernel.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_BOOL_LOGICAL_AND_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_BOOL_LOGICAL_AND_KERNEL_H + +#include "TestDataLogicalAndBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace logical_and_bool +{ +/* + * LogicalAnd Kernel: + * + * Input(1, 4, 4, 3) Input(1, 4, 4, 3) + * | | + * LogicalAnd + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + true, false, true, true, true, false, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, false, false}; +const std::vector input2_data = { + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, false}; + +const std::vector reference_output_data = { + true, false, true, true, true, false, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, false, false, false}; + +} // namespace logical_and_bool + +class TestDataBoolLogicalAnd : public TestDataLogicalAndBase +{ +public: + TestDataBoolLogicalAnd() + { + _input1_data = logical_and_bool::input1_data; + _input2_data = logical_and_bool::input2_data; + _reference_output_data = logical_and_bool::reference_output_data; + _test_kernel_model_circle = logical_and_bool::test_kernel_model_circle; + } + + ~TestDataBoolLogicalAnd() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_LOGICAL_AND_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/NegLogicalAndKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/NegLogicalAndKernel.h new file mode 100644 index 0000000..6624fe2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/NegLogicalAndKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_AND_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_AND_KERNEL_H + +#include "TestDataLogicalAndBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_logical_and_inputs_type_mismatch +{ +/* + * LogicalAnd Kernel with input types mismatch: + * + * Input(1, 4, 4, 3)-Bool Input(1, 4, 4, 3)-Float32 + * | | + * LogicalAnd + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x56, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_logical_and_inputs_type_mismatch + +class NegTestDataInputTypeMismatchLogicalAndKernel : public NegTestDataBase +{ +public: + NegTestDataInputTypeMismatchLogicalAndKernel() + { + _test_kernel_model_circle = neg_logical_and_inputs_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputTypeMismatchLogicalAndKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_AND_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/TestDataLogicalAndBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/TestDataLogicalAndBase.h new file mode 100644 index 0000000..c2842a3 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_and/TestDataLogicalAndBase.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LOGICAL_AND_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LOGICAL_AND_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLogicalAndBase : public TestDataBase +{ +public: + TestDataLogicalAndBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LOGICAL_AND_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/BoolLogicalOrKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/BoolLogicalOrKernel.h new file mode 100644 index 0000000..90b7511 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/BoolLogicalOrKernel.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_BOOL_LOGICAL_OR_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_BOOL_LOGICAL_OR_KERNEL_H + +#include "TestDataLogicalOrBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace logical_or_bool +{ +/* + * LogicalOr Kernel: + * + * Input(1, 4, 4, 3) Input(1, 4, 4, 3) + * | | + * LogicalOr + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + true, false, true, true, true, false, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, false, false}; +const std::vector input2_data = { + true, false, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, false, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, false, false, false}; + +const std::vector reference_output_data = { + true, false, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, false, false}; + +} // namespace logical_or_bool + +class TestDataBoolLogicalOr : public TestDataLogicalOrBase +{ +public: + TestDataBoolLogicalOr() + { + _input1_data = logical_or_bool::input1_data; + _input2_data = logical_or_bool::input2_data; + _reference_output_data = logical_or_bool::reference_output_data; + _test_kernel_model_circle = logical_or_bool::test_kernel_model_circle; + } + + ~TestDataBoolLogicalOr() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_LOGICAL_OR_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/NegLogicalOrKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/NegLogicalOrKernel.h new file mode 100644 index 0000000..1225d98 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/NegLogicalOrKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_OR_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_OR_KERNEL_H + +#include "TestDataLogicalOrBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_logical_or_inputs_type_mismatch +{ +/* + * LogicalOr Kernel with input types mismatch: + * + * Input(1, 4, 4, 3)-Bool Input(1, 4, 4, 3)-Float32 + * | | + * LogicalOr + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x54, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_logical_or_inputs_type_mismatch + +class NegTestDataInputTypeMismatchLogicalOrKernel : public NegTestDataBase +{ +public: + NegTestDataInputTypeMismatchLogicalOrKernel() + { + _test_kernel_model_circle = neg_logical_or_inputs_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputTypeMismatchLogicalOrKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_LOGICAL_OR_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/TestDataLogicalOrBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/TestDataLogicalOrBase.h new file mode 100644 index 0000000..af9fee2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logical_or/TestDataLogicalOrBase.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LOGICAL_OR_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LOGICAL_OR_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLogicalOrBase : public TestDataBase +{ +public: + TestDataLogicalOrBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LOGICAL_OR_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/FloatLogisticKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/FloatLogisticKernel.h new file mode 100644 index 0000000..23f9f7b --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/FloatLogisticKernel.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_LOGISTIC_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_LOGISTIC_KERNEL_H + +#include "TestDataLogisticBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace logistic_float +{ +/* + * Logistic Kernel: + * + * Input(1, 3, 3, 2) + * | + * Logistic + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {29.353455, 12.060211, 11.372606, -9.009369, 3.0267563, + 5.1447716, 21.289762, 19.976126, 8.726238, 4.8797092, + 3.64571, 34.80062, -6.9072685, -2.2714958, -16.44065, + 0.334301, -20.372694, 4.1522675}; + +const std::vector reference_output_data = { + 1.0, 0.99999416, 0.99998844, 0.00012225899, 0.9537683, 0.994204, + 1.0, 1.0, 0.99983776, 0.9924581, 0.97456115, 1.0, + 0.0009994869, 0.093511336, 7.2429586e-08, 0.5828055, 1.4198792e-09, 0.98451483}; + +} // namespace logistic_float + +class TestDataFloatLogistic : public TestDataLogisticBase +{ +public: + TestDataFloatLogistic() + { + _input_data = logistic_float::input_data; + _reference_output_data = logistic_float::reference_output_data; + _test_kernel_model_circle = logistic_float::test_kernel_model_circle; + } + + ~TestDataFloatLogistic() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_LOGISTIC_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/NegLogisticKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/NegLogisticKernel.h new file mode 100644 index 0000000..fec9689 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/NegLogisticKernel.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_LOGISTIC_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_LOGISTIC_KERNEL_H + +#include "TestDataLogisticBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_logistic_input_output_type_mismatch +{ +/* + * Logistic Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * Logistic + * | + * Output(1, 3, 3, 2) - Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_logistic_input_output_type_mismatch + +namespace neg_logistic_no_quant_params +{ +/* + * Logistic Kernel with UINT8 type and without quant params: + * + * Input(1, 3, 3, 2) - UINT8 + * | + * Logistic (no quant params) + * | + * Output(1, 3, 3, 2) - UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_logistic_no_quant_params + +class NegTestDataInputOutputTypeMismatchLogisticKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchLogisticKernel() + { + _test_kernel_model_circle = neg_logistic_input_output_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchLogisticKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataNoQuantParamsLogisticKernel : public NegTestDataBase +{ +public: + NegTestDataNoQuantParamsLogisticKernel() + { + _test_kernel_model_circle = neg_logistic_no_quant_params::test_kernel_model_circle; + } + + ~NegTestDataNoQuantParamsLogisticKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_LOGISTIC_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/TestDataLogisticBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/TestDataLogisticBase.h new file mode 100644 index 0000000..faffd2f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/logistic/TestDataLogisticBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_LOGISTIC_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_LOGISTIC_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataLogisticBase : public TestDataBase +{ +public: + TestDataLogisticBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_LOGISTIC_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/FloatMaxPool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/FloatMaxPool2DKernel.h new file mode 100644 index 0000000..927859f --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/FloatMaxPool2DKernel.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_MAXPOOL2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_MAXPOOL2D_KERNEL_H + +#include "TestDataMaxPool2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace maxpool2d_float +{ +/* + * maxpool2d Kernel: + * + * Input(1, 3, 5, 1) + * | + * MaxPool2D + * | + * Output(1, 2, 2, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x39, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd0, 0xfe, 0xff, 0xff, 0xd4, 0xfe, 0xff, 0xff, 0xd8, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xba, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, + 0x11, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, + 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x78, 0x3a, 0x30, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 1, -1, 0, -2, 2, // + -7, -6, -5, -4, -3, // + 5, 4, 3, 6, 7, // +}; + +const std::vector reference_output_data{ + 1, 2, // + 5, 7, // +}; + +} // namespace maxpool2d_float + +class TestDataFloatMaxPool2D : public TestDataMaxPool2DBase +{ +public: + TestDataFloatMaxPool2D() + { + _input_data = maxpool2d_float::input_data; + _reference_output_data = maxpool2d_float::reference_output_data; + _test_kernel_model_circle = maxpool2d_float::test_kernel_model_circle; + } + + ~TestDataFloatMaxPool2D() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_MAXPOOL2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/NegMaxPool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/NegMaxPool2DKernel.h new file mode 100644 index 0000000..dbba736 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/NegMaxPool2DKernel.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_MAXPOOL2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_MAXPOOL2D_KERNEL_H + +#include "TestDataMaxPool2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_maxpool2d_kernel +{ +/* + * maxpool2d Kernel with input_type != output_type: + * + * Input(1, 8, 8, 1) = Float32 + * | + * MaxPool2D + * | + * Output(1, 7, 7, 1) = Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_maxpool2d_kernel + +namespace neg_invalid_input_shape_maxpool2d_kernel +{ +/* + * maxpool2d Kernel with invalid input shape rank=5 (should be == 4): + * + * Input(1, 1, 8, 8, 1) = Int32 + * | + * MaxPool2D + * | + * Output(1, 7, 7, 1) = Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_invalid_input_shape_maxpool2d_kernel + +namespace neg_no_quant_params_maxpool2d_kernel +{ +/* + * maxpool2d Kernel S16 without quant params: + * + * Input(1, 1, 8, 8, 1) = INT16 + * | + * MaxPool2D (no quant params) + * | + * Output(1, 7, 7, 1) = INT16 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_no_quant_params_maxpool2d_kernel + +class NegTestDataInputOutputTypeMismatchMaxPool2DKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchMaxPool2DKernel() + { + _test_kernel_model_circle = neg_maxpool2d_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchMaxPool2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidInputShapeMaxPool2DKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputShapeMaxPool2DKernel() + { + _test_kernel_model_circle = neg_invalid_input_shape_maxpool2d_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputShapeMaxPool2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataNoQuantParamsMaxPool2DKernel : public NegTestDataBase +{ +public: + NegTestDataNoQuantParamsMaxPool2DKernel() + { + _test_kernel_model_circle = neg_no_quant_params_maxpool2d_kernel::test_kernel_model_circle; + } + + ~NegTestDataNoQuantParamsMaxPool2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_MAXPOOL2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/TestDataMaxPool2DBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/TestDataMaxPool2DBase.h new file mode 100644 index 0000000..71a81ce --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/maxpool2d/TestDataMaxPool2DBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_MAXPOOL2D_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_MAXPOOL2D_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataMaxPool2DBase : public TestDataBase +{ +public: + TestDataMaxPool2DBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_MAXPOOL2D_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/FloatMulKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/FloatMulKernel.h new file mode 100644 index 0000000..444bb53 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/FloatMulKernel.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_FLOAT_H + +#include "TestDataMulBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace mul_float_with_broadcasting +{ + +/* + * Mul Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Mul(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x4d, 0x75, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-16.302355, -4.020832, -7.797722, -10.514711, -13.203278, + -4.742243, 14.114815, 13.727003, 7.3895016, -2.0813313}; +const std::vector input2_data = {-3.132759, 9.31464}; +const std::vector reference_output_data = {51.07135, 12.596298, 24.428385, 32.940056, + 41.362686, -44.172283, 131.47443, 127.86209, + 68.83054, -19.386852}; + +} // namespace mul_float_with_broadcasting + +namespace mul_float_no_broadcasting +{ +/* + * Mul Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Mul(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x4d, 0x75, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {18.033651, -15.601158, 3.842373, 17.90259, -12.840965, + 19.272898, 22.070192, 12.965511, 23.20587, -7.0852413}; +std::vector input2_data = {20.24965, 4.263008, 16.145443, 25.501696, -8.905457, + -2.0830078, 28.85225, 24.545036, -13.7073345, 9.774281}; +std::vector reference_output_data = {365.1751, -66.507866, 62.03681, 456.5464, + 114.35466, -40.145596, 636.77466, 318.23895, + -318.0906, -69.253136}; + +} // namespace mul_float_no_broadcasting + +class TestDataFloatMul : public TestDataMulBase +{ +public: + explicit TestDataFloatMul(bool is_with_broadcast) : TestDataMulBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = mul_float_with_broadcasting::input1_data; + _input2_data = mul_float_with_broadcasting::input2_data; + _reference_output_data = mul_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = mul_float_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = mul_float_no_broadcasting::input1_data; + _input2_data = mul_float_no_broadcasting::input2_data; + _reference_output_data = mul_float_no_broadcasting::reference_output_data; + _test_kernel_model_circle = mul_float_no_broadcasting::test_kernel_model_circle; + } + } + + ~TestDataFloatMul() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/IntMulKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/IntMulKernel.h new file mode 100644 index 0000000..7dc1c53 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/IntMulKernel.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_INT_H + +#include "TestDataMulBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace mul_int_with_broadcasting +{ + +/* + * Mul Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Mul(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-5, 5, 5, -4, -4, 15, -13, 23, 5, 5}; +const std::vector input2_data = {-2, 14}; +const std::vector reference_output_data = {10, -10, -10, 8, 8, 210, -182, 322, 70, 70}; + +} // namespace mul_int_with_broadcasting + +namespace mul_int_no_broadcasting +{ +/* + * Mul Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Mul(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x4d, 0x75, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {5, 3, -3, 5, 14, 14, -3, 33, -11, 24}; +std::vector input2_data = {5, -3, 5, 5, 25, 5, -4, -2, 5, 25}; +std::vector reference_output_data = {25, -9, -15, 25, 350, 70, 12, -66, -55, 600}; + +} // namespace mul_int_no_broadcasting + +class TestDataIntMul : public TestDataMulBase +{ +public: + explicit TestDataIntMul(bool is_with_broadcast) : TestDataMulBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = mul_int_with_broadcasting::input1_data; + _input2_data = mul_int_with_broadcasting::input2_data; + _reference_output_data = mul_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = mul_int_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = mul_int_no_broadcasting::input1_data; + _input2_data = mul_int_no_broadcasting::input2_data; + _reference_output_data = mul_int_no_broadcasting::reference_output_data; + _test_kernel_model_circle = mul_int_no_broadcasting::test_kernel_model_circle; + } + } + + ~TestDataIntMul() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/NegMulKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/NegMulKernel.h new file mode 100644 index 0000000..cbbdbdd --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/NegMulKernel.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_MUL_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_MUL_KERNEL_H + +#include "TestDataMulBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace input_1_wrong_type +{ + +/* + * Mul Kernel with input type mismatch: + * + * Input_1(2, 5) - Int32 Input_2(2, 1) - Float + * \ / + * Mul(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace input_1_wrong_type + +namespace input_2_wrong_type +{ + +/* + * Mul Kernel with input type mismatch: + * + * Input_1(2, 5)- Float Input_2(2, 1) - Int32 + * \ / + * Mul(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace input_2_wrong_type + +namespace unsupported_type +{ + +/* + * Mul Kernel with unsupported type: + * + * Input_1(2, 5)- Int16 Input_2(2, 1) - Int16 + * \ / + * Mul(with broadcast) + * | + * Output(2, 5) - Int16 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xd4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace unsupported_type + +class NegTestDataInput1WrongTypeMul : public NegTestDataBase +{ +public: + NegTestDataInput1WrongTypeMul() + { + _test_kernel_model_circle = input_1_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataInput1WrongTypeMul() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInput2WrongTypeMul : public NegTestDataBase +{ +public: + NegTestDataInput2WrongTypeMul() + { + _test_kernel_model_circle = input_2_wrong_type::test_kernel_model_circle; + } + + ~NegTestDataInput2WrongTypeMul() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInt16TypeMul : public NegTestDataBase +{ +public: + NegTestDataInt16TypeMul() + { + _test_kernel_model_circle = unsupported_type::test_kernel_model_circle; + } + + ~NegTestDataInt16TypeMul() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_MUL_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/TestDataMulBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/TestDataMulBase.h new file mode 100644 index 0000000..1b64982 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/mul/TestDataMulBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataMulBase : public TestDataBase +{ +public: + explicit TestDataMulBase(bool) + { + // Do nothing + } + + TestDataMulBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_MUL_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/FloatNegKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/FloatNegKernel.h new file mode 100644 index 0000000..3e35666 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/FloatNegKernel.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_NEG_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_NEG_KERNEL_H + +#include "TestDataNegBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_float +{ +/* + * Neg Kernel: + * + * Input(1, 3, 3, 2) + * | + * Neg + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2a, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-2.3082886, 5.8998604, 7.297842, 30.999863, 15.692827, + -18.824865, 22.614136, 5.7466774, 6.65571, -1.1786385, + 3.8724442, 9.483013, 19.376131, -6.1562176, -5.4431114, + 9.304043, 22.674402, -2.3587227}; + +const std::vector reference_output_data = { + 2.3082886, -5.8998604, -7.297842, -30.999863, -15.692827, 18.824865, + -22.614136, -5.7466774, -6.65571, 1.1786385, -3.8724442, -9.483013, + -19.376131, 6.1562176, 5.4431114, -9.304043, -22.674402, 2.3587227}; + +} // namespace neg_float + +class TestDataFloatNeg : public TestDataNegBase +{ +public: + TestDataFloatNeg() + { + _input_data = neg_float::input_data; + _reference_output_data = neg_float::reference_output_data; + _test_kernel_model_circle = neg_float::test_kernel_model_circle; + } + + ~TestDataFloatNeg() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_NEG_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/NegNegKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/NegNegKernel.h new file mode 100644 index 0000000..43f9c09 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/NegNegKernel.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_NEG_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_NEG_KERNEL_H + +#include "TestDataNegBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_neg_kernel +{ +/* + * Negate Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * Negate + * | + * Output(1, 3, 3, 2) - Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2a, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_input_output_type_mismatch_neg_kernel + +namespace neg_invalid_input_shape_neg_kernel +{ +/* + * Nagate Kernel with invalid input shape rank=5 (should be == 4): + * + * Input(1, 1, 8, 8, 1) = Float32 + * | + * Negate + * | + * Output(1, 7, 7, 1) = Float32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2a, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_invalid_input_shape_neg_kernel + +class NegTestDataInputOutputTypeMismatchNegKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchNegKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_neg_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchNegKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidInputShapeNegKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputShapeNegKernel() + { + _test_kernel_model_circle = neg_invalid_input_shape_neg_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputShapeNegKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_NEG_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/TestDataNegBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/TestDataNegBase.h new file mode 100644 index 0000000..fcb5704 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/neg/TestDataNegBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataNegBase : public TestDataBase +{ +public: + TestDataNegBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/FloatNotEqualKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/FloatNotEqualKernel.h new file mode 100644 index 0000000..51fd85d --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/FloatNotEqualKernel.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_FLOAT_H + +#include "TestDataNotEqualBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace not_equal_float +{ + +/* + * NotEqual Kernel: + * + * Input_1(1, 4, 4, 3) Input_2(1, 4, 4, 3) + * \ / + * NotEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = { + -0.01787583, -0.8314556, -0.47624078, -1.9747407, -0.51676583, -0.20183715, -1.9358647, + 0.7616414, -0.0899037, 2.048367, -1.3174965, 1.5267943, 0.68707687, 1.3464743, + 0.98674047, -1.4853697, 1.9973947, 0.5170953, 0.37471953, -1.6011852, 0.32045737, + -0.6598305, -1.7946662, 1.2349467, 1.3320708, 0.5151753, 1.345111, -0.16560331, + 0.82792366, -1.734876, 0.043626763, -0.0118546495, 0.31535238, 0.1888555, -0.32523626, + -0.997665, 0.5819472, -2.3194845, -1.6897905, 0.9981752, -1.2897044, 0.75768864, + 0.56781554, -1.0565805, -1.4891449, 0.2493645, -1.1312587, 0.6837854}; + +const std::vector input2_data = { + 0.30809638, -0.28285328, -0.8437058, 1.7689779, 0.5182942, 0.571205, -0.89484423, + 0.28100377, 0.5453497, 1.3848042, -0.04359268, -1.7448778, -0.5375435, -0.85059136, + -0.77961826, -0.4916915, 1.3359088, -0.09580261, 0.6158275, -0.05056348, 0.90505254, + 0.94226706, 1.136139, -0.45077038, -0.5018571, -1.1543767, 0.85094684, -0.13731039, + -0.3298641, 0.9474698, -0.48497504, -0.14864737, -0.009302358, -1.1259161, 0.44226727, + 1.0149708, 0.36024934, 0.4969523, 0.45014778, -0.34718898, 1.2260172, 0.35304692, + -1.3037513, -0.2565706, 0.18085766, -0.7099202, -0.9203537, -1.2257448}; + +const std::vector reference_output_data = { + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}; + +} // namespace not_equal_float + +namespace neg_not_equal_float_with_no_broadcasting +{ + +/* + * NotEqual Kernel with input type mismatch: + * + * Input_1(1, 4, 4, 3)-float Input_2(1, 4, 4, 3)-int + * \ / + * NotEqual(no broadcast) + * | + * Output(1, 4, 4, 3) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {}; + +const std::vector input2_data = {}; + +const std::vector reference_output_data = {}; + +} // namespace neg_not_equal_float_with_no_broadcasting + +class TestDataFloatNotEqual : public TestDataNotEqualBase +{ +public: + explicit TestDataFloatNotEqual(bool is_with_broadcast, bool is_neg) + : TestDataNotEqualBase(is_with_broadcast) + { + if (is_with_broadcast) + { + assert(false && "Not impl yet"); + } + else + { + if (is_neg) + { + _input1_data = neg_not_equal_float_with_no_broadcasting::input1_data; + _input2_data = neg_not_equal_float_with_no_broadcasting::input2_data; + _reference_output_data = neg_not_equal_float_with_no_broadcasting::reference_output_data; + _test_kernel_model_circle = + neg_not_equal_float_with_no_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = not_equal_float::input1_data; + _input2_data = not_equal_float::input2_data; + _reference_output_data = not_equal_float::reference_output_data; + _test_kernel_model_circle = not_equal_float::test_kernel_model_circle; + } + } + } + + ~TestDataFloatNotEqual() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/TestDataNotEqualBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/TestDataNotEqualBase.h new file mode 100644 index 0000000..786be21 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/notequal/TestDataNotEqualBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataNotEqualBase : public TestDataBase +{ +public: + explicit TestDataNotEqualBase(bool) + { + // Do nothing + } + + TestDataNotEqualBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NOT_EQUAL_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/PackKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/PackKernel.h new file mode 100644 index 0000000..22d9f66 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/PackKernel.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_H + +#include "TestDataPackBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace pack_float +{ +/* + * Pack Kernel: + * + * Input(2, 4, 3) Input(2, 4, 3) + * \ / + * \ / + * Pack + * | + * Output(2, 2, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x63, + 0x6b, 0x5f, 0x34, 0x64, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x31, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data_1 = { + -0.17432976, -10.103649, 4.2064724, -7.185501, 7.6475716, -34.405083, 14.065273, -17.566177, + 16.921495, -8.886711, 16.913736, -8.991537, 18.480549, 17.71526, -3.8370514, 16.570705, + -14.831467, 17.709942, 0.026670456, -6.250948, 10.977406, 22.907639, -37.32604, -1.3433037}; + +const std::vector input_data_2 = { + -22.672482, 10.947399, -9.828194, -3.2829914, 14.490927, 24.998316, 33.86125, -17.046562, + 7.1629715, 21.064964, 5.813303, -16.67994, -22.828697, -7.9325237, -23.776447, -17.539246, + -3.8784523, 14.898129, 27.151598, -3.9495945, 21.426613, -8.786135, 0.22362137, -7.534506}; + +const std::vector reference_output_data = { + -1.7432976e-01, -1.0103649e+01, 4.2064724e+00, -7.1855011e+00, 7.6475716e+00, -3.4405083e+01, + 1.4065273e+01, -1.7566177e+01, 1.6921495e+01, -8.8867111e+00, 1.6913736e+01, -8.9915371e+00, + -2.2672482e+01, 1.0947399e+01, -9.8281937e+00, -3.2829914e+00, 1.4490927e+01, 2.4998316e+01, + 3.3861252e+01, -1.7046562e+01, 7.1629715e+00, 2.1064964e+01, 5.8133030e+00, -1.6679939e+01, + 1.8480549e+01, 1.7715260e+01, -3.8370514e+00, 1.6570705e+01, -1.4831467e+01, 1.7709942e+01, + 2.6670456e-02, -6.2509480e+00, 1.0977406e+01, 2.2907639e+01, -3.7326038e+01, -1.3433037e+00, + -2.2828697e+01, -7.9325237e+00, -2.3776447e+01, -1.7539246e+01, -3.8784523e+00, 1.4898129e+01, + 2.7151598e+01, -3.9495945e+00, 2.1426613e+01, -8.7861347e+00, 2.2362137e-01, -7.5345058e+00}; +} // namespace pack_float + +namespace pack_int +{ +/* + * Pack Kernel: + * + * Input(2, 8) Input(2, 8) + * \ / + * \ / + * Pack + * | + * Output(2, 2, 8) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xb8, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x6a, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xfe, 0xff, 0xff, 0xa8, 0xfe, 0xff, 0xff, + 0xac, 0xfe, 0xff, 0xff, 0xb0, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3b, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, + 0x7c, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data_1 = {-5, 5, -5, -5, 15, 13, -4, 14, + 5, 5, 5, 5, 12, 21, -5, 6}; +const std::vector input_data_2 = {4, 5, 5, 5, 5, 5, 15, 4, -3, 11, 15, 32, -13, 13, -3, 5}; + +const std::vector reference_output_data = {-5, 5, -5, -5, 15, 13, -4, 14, 5, 5, 5, + 5, 12, 21, -5, 6, 4, 5, 5, 5, 5, 5, + 15, 4, -3, 11, 15, 32, -13, 13, -3, 5}; + +} // namespace pack_int + +namespace pack_quant_u8 +{ +/* + * Pack Kernel: + * + * Input(2, 4, 3) Input(2, 4, 3) + * \ / + * \ / + * Pack + * | + * Output(2, 2, 4, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x00, 0x00, 0x00, + 0x0c, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x34, 0x64, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x31, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data_1 = {5, 243, 251, 5, 6, 7, 13, 23, 23, 5, 13, 5, + 7, 244, 13, 5, 5, 244, 252, 253, 5, 5, 5, 5}; + +const std::vector input_data_2 = {15, 30, 252, 7, 252, 40, 245, 13, 13, 14, 21, 5, + 5, 245, 251, 5, 251, 223, 5, 251, 22, 15, 15, 15}; + +const std::vector reference_output_data = { + 5, 243, 251, 5, 6, 7, 13, 23, 23, 5, 13, 5, 15, 30, 252, 7, + 252, 40, 245, 13, 13, 14, 21, 5, 7, 244, 13, 5, 5, 244, 252, 253, + 5, 5, 5, 5, 5, 245, 251, 5, 251, 223, 5, 251, 22, 15, 15, 15}; + +} // namespace pack_quant_u8 + +class TestDataFloatPack : public TestDataPackBase +{ +public: + TestDataFloatPack() + { + _input_data_1 = pack_float::input_data_1; + _input_data_2 = pack_float::input_data_2; + _reference_output_data = pack_float::reference_output_data; + _test_kernel_model_circle = pack_float::test_kernel_model_circle; + } + + ~TestDataFloatPack() override = default; +}; + +class TestDataIntPack : public TestDataPackBase +{ +public: + TestDataIntPack() + { + _input_data_1 = pack_int::input_data_1; + _input_data_2 = pack_int::input_data_2; + _reference_output_data = pack_int::reference_output_data; + _test_kernel_model_circle = pack_int::test_kernel_model_circle; + } + + ~TestDataIntPack() override = default; +}; + +class TestDataQuantU8Pack : public TestDataPackBase +{ +public: + TestDataQuantU8Pack() + { + _input_data_1 = pack_quant_u8::input_data_1; + _input_data_2 = pack_quant_u8::input_data_2; + _reference_output_data = pack_quant_u8::reference_output_data; + _test_kernel_model_circle = pack_quant_u8::test_kernel_model_circle; + } + + ~TestDataQuantU8Pack() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/TestDataPackBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/TestDataPackBase.h new file mode 100644 index 0000000..b587df3 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pack/TestDataPackBase.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataPackBase : public TestDataBase +{ +public: + TestDataPackBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data_1; + case 1: + return _input_data_2; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data_1; + std::vector _input_data_2; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_PACK_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/FloatPadKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/FloatPadKernel.h new file mode 100644 index 0000000..ddfd3cf --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/FloatPadKernel.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_PAD_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_PAD_KERNEL_H + +#include "TestDataPadBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace pad_float +{ +/* + * Pad Kernel: + * + * Input(1, 3, 3, 2) + * | + * Pad + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -0.7942257, -1.3318212, -0.7918672, -1.0024637, -0.23364098, 0.49224994, + -0.23747201, -0.14768714, 1.4870708, -0.79761434, -0.27848604, 1.1856802, + 1.1039438, -0.34465268, -1.5857629, 3.0654314, 0.13304773, 0.067413524}; + +const std::vector reference_output_data = { + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -0.7942257, -1.3318212, -0.7918672, + -1.0024637, -0.23364098, 0.49224994, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, -0.23747201, -0.14768714, 1.4870708, + -0.79761434, -0.27848604, 1.1856802, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.1039438, -0.34465268, -1.5857629, + 3.0654314, 0.13304773, 0.067413524, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + +} // namespace pad_float + +class TestDataFloatPad : public TestDataPadBase +{ +public: + TestDataFloatPad() + { + _input_data = pad_float::input_data; + _reference_output_data = pad_float::reference_output_data; + _test_kernel_model_circle = pad_float::test_kernel_model_circle; + } + + ~TestDataFloatPad() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_PAD_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/NegPadKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/NegPadKernel.h new file mode 100644 index 0000000..15936e4 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/NegPadKernel.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_PAD_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_PAD_KERNEL_H + +#include "TestDataPadBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_pad_input_output_type_mismatch +{ +/* + * Pad Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * Pad + * | + * Output(1, 3, 3, 2) - Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_pad_input_output_type_mismatch + +class NegTestDataInputOutputTypeMismatchPadKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchPadKernel() + { + _test_kernel_model_circle = neg_pad_input_output_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchPadKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_PAD_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/TestDataPadBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/TestDataPadBase.h new file mode 100644 index 0000000..e5c72de --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad/TestDataPadBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_PAD_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_PAD_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataPadBase : public TestDataBase +{ +public: + TestDataPadBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_PAD_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/FloatPadV2Kernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/FloatPadV2Kernel.h new file mode 100644 index 0000000..0aa00f8 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/FloatPadV2Kernel.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_PADV2_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_PADV2_KERNEL_H + +#include "TestDataPadV2Base.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace padV2_float +{ +/* + * PadV2 Kernel: + * + * Input(1, 3, 3, 2) + * | + * PadV2 + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x70, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6e, 0x67, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -0.7942257, -1.3318212, -0.7918672, -1.0024637, -0.23364098, 0.49224994, + -0.23747201, -0.14768714, 1.4870708, -0.79761434, -0.27848604, 1.1856802, + 1.1039438, -0.34465268, -1.5857629, 3.0654314, 0.13304773, 0.067413524}; + +const std::vector reference_output_data = { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, -0.7942257, -1.3318212, -0.7918672, + -1.0024637, -0.23364098, 0.49224994, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, -0.23747201, -0.14768714, 1.4870708, + -0.79761434, -0.27848604, 1.1856802, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.1039438, -0.34465268, -1.5857629, + 3.0654314, 0.13304773, 0.067413524, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + +} // namespace padV2_float + +class TestDataFloatPadV2 : public TestDataPadV2Base +{ +public: + TestDataFloatPadV2() + { + _input_data = padV2_float::input_data; + _reference_output_data = padV2_float::reference_output_data; + _test_kernel_model_circle = padV2_float::test_kernel_model_circle; + } + + ~TestDataFloatPadV2() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_PADV2_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/NegPadV2Kernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/NegPadV2Kernel.h new file mode 100644 index 0000000..2591a60 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/NegPadV2Kernel.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_PADV2_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_PADV2_KERNEL_H + +#include "TestDataPadV2Base.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_padV2_input_output_type_mismatch +{ +/* + * PadV2 Kernel with input output type mismatch (should be equal): + * + * Input(1, 3, 3, 2) - Float + * | + * PadV2 + * | + * Output(1, 3, 3, 2) - Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0xe8, 0x01, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x9c, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_padV2_input_output_type_mismatch + +class NegTestDataInputOutputTypeMismatchPadV2Kernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchPadV2Kernel() + { + _test_kernel_model_circle = neg_padV2_input_output_type_mismatch::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchPadV2Kernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_PADV2_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/TestDataPadV2Base.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/TestDataPadV2Base.h new file mode 100644 index 0000000..b94eb3c --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/pad_v2/TestDataPadV2Base.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_PADV2_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_PADV2_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataPadV2Base : public TestDataBase +{ +public: + TestDataPadV2Base() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_PADV2_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/NegReduceProdKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/NegReduceProdKernel.h new file mode 100644 index 0000000..73d996b --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/NegReduceProdKernel.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_REDUCE_PROD_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_REDUCE_PROD_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_wrong_type_mismatch_reduce_prod_kernel +{ +/* + * ReduceProd Kernel with wrong input type: + * + * Input(5, 5) - Int16 Axis(1) + * \ / + * ReduceProd(keep_dims=false) + * | + * Output(5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x72, 0x65, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x51, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_wrong_type_mismatch_reduce_prod_kernel + +namespace neg_axis_wrong_type_mismatch_reduce_prod_kernel +{ +/* + * ReduceProd Kernel with wrong axis type: + * + * Input(5, 5) Axis(1) - Float32 + * \ / + * ReduceProd(keep_dims=false) + * | + * Output(5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x6c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x40, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x72, 0x65, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_axis_wrong_type_mismatch_reduce_prod_kernel + +class NegTestDataWrongInputTypeReduceProdKernel : public NegTestDataBase +{ +public: + NegTestDataWrongInputTypeReduceProdKernel() + { + _test_kernel_model_circle = + neg_input_wrong_type_mismatch_reduce_prod_kernel::test_kernel_model_circle; + } + + ~NegTestDataWrongInputTypeReduceProdKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataWrongAxisTypeReduceProdKernel : public NegTestDataBase +{ +public: + NegTestDataWrongAxisTypeReduceProdKernel() + { + _test_kernel_model_circle = + neg_axis_wrong_type_mismatch_reduce_prod_kernel::test_kernel_model_circle; + } + + ~NegTestDataWrongAxisTypeReduceProdKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_REDUCE_PROD_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/ReduceProdKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/ReduceProdKernel.h new file mode 100644 index 0000000..be0a19a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/ReduceProdKernel.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_REDUCE_PROD_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_REDUCE_PROD_KERNEL_H + +#include "TestDataReduceCommonBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace reduce_prod_float +{ +/* + * ReduceProd Kernel: + * + * Input(5, 5) Axis(1) + * \ / + * ReduceProd(keep_dims=false) + * | + * Output(5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xd0, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0xda, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x92, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x50, 0x72, 0x6f, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x14, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x51, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {11.411349, -16.127048, 2.1805973, 2.4134026, -24.384453, + 7.066084, -2.4375877, -6.3261166, 12.296496, -5.2269707, + -1.1958504, 39.85154, 11.011908, -15.1922455, -5.623905, + 12.9133, 17.127638, -5.8921337, 32.048306, -1.0499363, + 3.921646, -0.9553833, 0.16646576, -19.362396, 2.1621552}; + +const std::vector reference_output_data = {-4.8831299e+03, -2.5635121e+04, 1.4899535e+02, + 2.7976750e+05, 1.6272373e+03}; +} // namespace reduce_prod_float + +namespace reduce_prod_int +{ +/* + * ReduceProd Kernel: + * + * Input(5, 5) Axis(1) + * \ / + * ReduceProd(keep_dims=false) + * | + * Output(5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xd0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xbc, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0xda, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xfe, 0xff, 0xff, 0xd4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9e, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x90, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x50, 0x72, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xce, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, + 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-2, -3, -5, 15, -11, 5, 5, 5, 7, 15, -12, 5, 5, + -3, 15, 22, -5, 24, 5, -13, -6, -4, -5, -3, 24}; + +const std::vector reference_output_data = {-15840, -1500, 15000, 4725, 772200}; +} // namespace reduce_prod_int + +class TestDataFloatReduceProd : public TestDataReduceCommonBase +{ +public: + TestDataFloatReduceProd() + { + _input_data = reduce_prod_float::input_data; + _reference_output_data = reduce_prod_float::reference_output_data; + _test_kernel_model_circle = reduce_prod_float::test_kernel_model_circle; + } + + ~TestDataFloatReduceProd() override = default; +}; + +class TestDataIntReduceProd : public TestDataReduceCommonBase +{ +public: + TestDataIntReduceProd() + { + _input_data = reduce_prod_int::input_data; + _reference_output_data = reduce_prod_int::reference_output_data; + _test_kernel_model_circle = reduce_prod_int::test_kernel_model_circle; + } + + ~TestDataIntReduceProd() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_REDUCE_PROD_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/TestDataReduceCommonBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/TestDataReduceCommonBase.h new file mode 100644 index 0000000..2cdddd7 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reduce_common/TestDataReduceCommonBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_REDUCE_COMMON_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_REDUCE_COMMON_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataReduceCommonBase : public TestDataBase +{ +public: + TestDataReduceCommonBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_REDUCE_COMMON_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/FloatReLUKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/FloatReLUKernel.h new file mode 100644 index 0000000..39cbdab --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/FloatReLUKernel.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU_KERNEL_H + +#include "TestDataReLUBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace relu_float +{ +/* + * ReLU Kernel: + * + * Input(1, 3, 3, 2) + * | + * ReLU + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {4.5651245, -1.783557, 10.147356, 1.359064, 19.900585, + 31.432447, 3.4538271, -3.425167, 11.351466, -2.519806, + -2.702178, -15.201234, 15.547801, 10.433272, 4.301023, + 5.4106083, 0.14018308, -16.32785}; +const std::vector reference_output_data = { + 4.5651245, 0.0, 10.147356, 1.359064, 19.900585, 31.432447, 3.4538271, 0.0, 11.351466, + 0.0, 0.0, 0.0, 15.547801, 10.433272, 4.301023, 5.4106083, 0.14018308, 0.0}; + +} // namespace relu_float + +class TestDataFloatReLU : public TestDataReLUBase +{ +public: + TestDataFloatReLU() + { + _input_data = relu_float::input_data; + _reference_output_data = relu_float::reference_output_data; + _test_kernel_model_circle = relu_float::test_kernel_model_circle; + } + + ~TestDataFloatReLU() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/NegReLUKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/NegReLUKernel.h new file mode 100644 index 0000000..54d1cb1 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/NegReLUKernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_RELU_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_RELU_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * ReLU Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * ReLU + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchReLUKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchReLUKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchReLUKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_RELU_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/TestDataReLUBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/TestDataReLUBase.h new file mode 100644 index 0000000..5e76fed --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu/TestDataReLUBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_RELU_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_RELU_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataReLUBase : public TestDataBase +{ +public: + TestDataReLUBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_RELU_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/FloatReLU6Kernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/FloatReLU6Kernel.h new file mode 100644 index 0000000..7be41c1 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/FloatReLU6Kernel.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU6_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU6_KERNEL_H + +#include "TestDataReLU6Base.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace relu6_float +{ +/* + * ReLU6 Kernel: + * + * Input(1, 3, 3, 2) + * | + * ReLU6 + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {4.2436867, 11.136094, -10.418385, 9.319618, -4.471156, + -20.418179, 18.783192, 7.1997013, -9.659637, 6.2115526, + -4.2794833, 10.500693, 8.646875, 3.8725555, -21.104343, + -7.6522045, 1.0404004, 7.109288}; +const std::vector reference_output_data = {4.2436867, 6.0, 0.0, 6.0, 0.0, 0.0, + 6.0, 6.0, 0.0, 6.0, 0.0, 6.0, + 6.0, 3.8725555, 0.0, 0.0, 1.0404004, 6.0}; + +} // namespace relu6_float + +class TestDataFloatReLU6 : public TestDataReLU6Base +{ +public: + TestDataFloatReLU6() + { + _input_data = relu6_float::input_data; + _reference_output_data = relu6_float::reference_output_data; + _test_kernel_model_circle = relu6_float::test_kernel_model_circle; + } + + ~TestDataFloatReLU6() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_RELU6_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/NegReLU6Kernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/NegReLU6Kernel.h new file mode 100644 index 0000000..e402d27 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/NegReLU6Kernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_RELU6_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_RELU6_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * ReLU6 Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * ReLU6 + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchReLU6Kernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchReLU6Kernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchReLU6Kernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_LAEKY_RELU6_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/TestDataReLU6Base.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/TestDataReLU6Base.h new file mode 100644 index 0000000..8e59832 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/relu6/TestDataReLU6Base.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_RELU6_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_RELU6_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataReLU6Base : public TestDataBase +{ +public: + TestDataReLU6Base() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_RELU6_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reshape/ReshapeKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reshape/ReshapeKernel.h new file mode 100644 index 0000000..60a42ff --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/reshape/ReshapeKernel.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_RESHAPE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_RESHAPE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_reshape_kernel +{ +/* + * Reshape Kernel with not const shape params + * + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa8, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-9.297554, 6.094736, 13.846724, -30.348026, 12.606297, + -25.089138, -18.258347, -8.119066, 0.24100876, 6.95887}; + +const std::vector reference_output_data = {-9.297554, 6.094736, 13.846724, -30.348026, + 12.606297, -25.089138, -18.258347, -8.119066, + 0.24100876, 6.95887}; + +} // namespace neg_reshape_kernel + +namespace reshape_kernel +{ +/* + * Reshape Kernel: + * + * Input(1, 1, 1, 10) Const([-1, 10]) + * \ / + * Reshape + * | + * Output(1, 10) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x0a, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa4, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-9.297554, 6.094736, 13.846724, -30.348026, 12.606297, + -25.089138, -18.258347, -8.119066, 0.24100876, 6.95887}; + +const std::vector reference_output_data = {-9.297554, 6.094736, 13.846724, -30.348026, + 12.606297, -25.089138, -18.258347, -8.119066, + 0.24100876, 6.95887}; + +} // namespace reshape_kernel + +template class TestDataReshapeKernel : public TestDataBase +{ +public: + TestDataReshapeKernel(bool is_neg) + { + if (not is_neg) + { + _input_data = reshape_kernel::input_data; + _reference_output_data = reshape_kernel::reference_output_data; + _test_kernel_model_circle = reshape_kernel::test_kernel_model_circle; + } + else + { + _input_data = neg_reshape_kernel::input_data; + _reference_output_data = neg_reshape_kernel::reference_output_data; + _test_kernel_model_circle = neg_reshape_kernel::test_kernel_model_circle; + } + } + + ~TestDataReshapeKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_RESHAPE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/FloatResizeBilinearKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/FloatResizeBilinearKernel.h new file mode 100644 index 0000000..0ada18e --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/FloatResizeBilinearKernel.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_RESIZE_BILINEAR_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_RESIZE_BILINEAR_KERNEL_H + +#include "TestDataResizeBilinearBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace resize_bilinear_float +{ +/* + * ResizeBilinear Kernel: + * + * align_corners = false; half_pixel_centers = false; + * + * Input(2, 2, 2, 1) FLOAT + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) FLOAT + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0xa0, 0x01, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, + 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, + 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, + 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00 + +}; + +const std::vector input_data = { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // +}; + +const std::vector reference_output_data = { + 3, 5, 6, // + 7, 9, 10, // + 9, 11, 12, // + 4, 8, 10, // + 8, 12, 14, // + 10, 14, 16, // +}; + +} // namespace resize_bilinear_float + +namespace resize_bilinear_float_half_pixel_centers +{ +/* + * ResizeBilinear Kernel: + * + * align_corners = false; half_pixel_centers = true; + * + * Input(2, 2, 2, 1) FLOAT + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) FLOAT + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 1, 2, // + 3, 4, // + 1, 2, // + 3, 4 // +}; + +const std::vector reference_output_data = { + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // +}; + +} // namespace resize_bilinear_float_half_pixel_centers + +class TestDataFloatResizeBilinear : public TestDataResizeBilinearBase +{ +public: + TestDataFloatResizeBilinear(bool half_pixel_centers) + { + if (!half_pixel_centers) + { + _input_data = resize_bilinear_float::input_data; + _reference_output_data = resize_bilinear_float::reference_output_data; + _test_kernel_model_circle = resize_bilinear_float::test_kernel_model_circle; + } + else + { + _input_data = resize_bilinear_float_half_pixel_centers::input_data; + _reference_output_data = resize_bilinear_float_half_pixel_centers::reference_output_data; + _test_kernel_model_circle = + resize_bilinear_float_half_pixel_centers::test_kernel_model_circle; + } + } + + ~TestDataFloatResizeBilinear() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_RESIZE_BILINEAR_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/NegResizeBilinearKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/NegResizeBilinearKernel.h new file mode 100644 index 0000000..70133dc --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/NegResizeBilinearKernel.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_RESIZE_BILINEAR_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_RESIZE_BILINEAR_KERNEL_H + +#include "TestDataResizeBilinearBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_invalid_input_shape_float_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid_input_shape, dimensions should be 4): + * + * align_corners = false; half_pixel_centers = true; + * + * Input(2, 2, 2) FLOAT + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) FLOAT + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace neg_invalid_input_shape_float_resize_bilinear_kernel + +namespace neg_invalid_param_float_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid params: should be only param true "align_corners" or + * "half_pixel_centers" ): + * + * align_corners = true; half_pixel_centers = true; + * + * Input(2, 2, 2, 1) FLOAT + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) FLOAT + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, + 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, + 0x00 + +}; + +} // namespace neg_invalid_param_float_resize_bilinear_kernel + +namespace neg_invalid_size_shape_dimensions_float_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid dimensions of the size shape ): + * + * align_corners = false; half_pixel_centers = false; + * + * Input(2, 2, 2, 1) FLOAT + * | + * | Constant Input(1) [3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) FLOAT + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00 + +}; + +} // namespace neg_invalid_size_shape_dimensions_float_resize_bilinear_kernel + +namespace neg_invalid_input_shape_uint8_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid_input_shape, dimensions should be 4): + * + * align_corners = false; half_pixel_centers = true; + * + * Input(2, 2, 2) UINT8 + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_invalid_input_shape_uint8_resize_bilinear_kernel + +namespace neg_invalid_param_uint8_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid params: should be only param true "align_corners" or + * "half_pixel_centers" ): + * + * align_corners = true; half_pixel_centers = true; + * + * Input(2, 2, 2, 1) UINT8 + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x3c, + 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, + 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x5a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, + 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, + 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00 + +}; +} // namespace neg_invalid_param_uint8_resize_bilinear_kernel + +namespace neg_invalid_size_shape_dimensions_uint8_resize_bilinear_kernel +{ +/* + * ResizeBilinear Kernel (invalid dimensions of the size shape ): + * + * align_corners = false; half_pixel_centers = false; + * + * Input(2, 2, 2, 1) UINT8 + * | + * | Constant Input(1) [3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x17, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00 + +}; + +} // namespace neg_invalid_size_shape_dimensions_uint8_resize_bilinear_kernel + +class NegTestDataInvalidInputShapeFloatResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputShapeFloatResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_input_shape_float_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputShapeFloatResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidParamFloatResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidParamFloatResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_param_float_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidParamFloatResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidSizeShapeDimensionsFloatResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidSizeShapeDimensionsFloatResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_size_shape_dimensions_float_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidSizeShapeDimensionsFloatResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidInputShapeUint8ResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidInputShapeUint8ResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_input_shape_uint8_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidInputShapeUint8ResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidParamUint8ResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidParamUint8ResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_param_uint8_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidParamUint8ResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInvalidSizeShapeDimensionsUint8ResizeBilinearKernel : public NegTestDataBase +{ +public: + NegTestDataInvalidSizeShapeDimensionsUint8ResizeBilinearKernel() + { + _test_kernel_model_circle = + neg_invalid_size_shape_dimensions_uint8_resize_bilinear_kernel::test_kernel_model_circle; + } + + ~NegTestDataInvalidSizeShapeDimensionsUint8ResizeBilinearKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_RESIZE_BILINEAR_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/TestDataResizeBilinearBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/TestDataResizeBilinearBase.h new file mode 100644 index 0000000..2180851 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/TestDataResizeBilinearBase.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_RESIZE_BILINEAR_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_RESIZE_BILINEAR_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataResizeBilinearBase : public TestDataBase +{ +public: + TestDataResizeBilinearBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_RESIZE_BILINEAR_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/U8ResizeBilinearKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/U8ResizeBilinearKernel.h new file mode 100644 index 0000000..7c422b1 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/resize_bilinear/U8ResizeBilinearKernel.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_UINT8_RESIZE_BILINEAR_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_UINT8_RESIZE_BILINEAR_KERNEL_H + +#include "TestDataResizeBilinearBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace resize_bilinear_uint8 +{ +/* + * ResizeBilinear Kernel: + * + * align_corners = false; half_pixel_centers = false; + * + * Input(2, 2, 2, 1) UINT8 + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00 + +}; + +const std::vector input_data = { + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // +}; + +const std::vector reference_output_data = { + 3, 5, 6, // + 7, 9, 10, // + 9, 11, 12, // + 4, 8, 10, // + 8, 12, 14, // + 10, 14, 16, // +}; + +} // namespace resize_bilinear_uint8 + +namespace resize_bilinear_uint8_half_pixel_centers +{ +/* + * ResizeBilinear Kernel: + * + * align_corners = false; half_pixel_centers = true; + * + * Input(2, 2, 2, 1) UINT8 + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x48, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0x30, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00 + +}; + +const std::vector input_data = { + 1, 2, // + 3, 4, // + 1, 2, // + 3, 4 // +}; + +const std::vector reference_output_data = { + 1, 2, 2, // + 2, 3, 3, // + 3, 4, 4, // + 1, 2, 2, // + 2, 3, 3, // + 3, 4, 4, // +}; + +} // namespace resize_bilinear_uint8_half_pixel_centers + +class TestDataUint8ResizeBilinear : public TestDataResizeBilinearBase +{ +public: + TestDataUint8ResizeBilinear(bool half_pixel_centers) + { + if (!half_pixel_centers) + { + _input_data = resize_bilinear_uint8::input_data; + _reference_output_data = resize_bilinear_uint8::reference_output_data; + _test_kernel_model_circle = resize_bilinear_uint8::test_kernel_model_circle; + } + else + { + _input_data = resize_bilinear_uint8_half_pixel_centers::input_data; + _reference_output_data = resize_bilinear_uint8_half_pixel_centers::reference_output_data; + _test_kernel_model_circle = + resize_bilinear_uint8_half_pixel_centers::test_kernel_model_circle; + } + } + + ~TestDataUint8ResizeBilinear() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_UINT8_RESIZE_BILINEAR_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/NegShapeKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/NegShapeKernel.h new file mode 100644 index 0000000..5c7737a --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/NegShapeKernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_SHAPE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_SHAPE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_output_wrong_type_shape_kernel +{ +/* + * Shape Kernel with wrong output type (should be INT32): + * + * Input(2, 3, 4) + * | + * Shape + * | + * Output(3) - FLOAT32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4d, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_output_wrong_type_shape_kernel + +class NegTestDataWrongOutputTypeShapeKernel : public NegTestDataBase +{ +public: + NegTestDataWrongOutputTypeShapeKernel() + { + _test_kernel_model_circle = neg_output_wrong_type_shape_kernel::test_kernel_model_circle; + } + + ~NegTestDataWrongOutputTypeShapeKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_SHAPE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/ShapeKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/ShapeKernel.h new file mode 100644 index 0000000..4be3f56 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/shape/ShapeKernel.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace shape_kernel +{ +/* + * Shape Kernel: + * + * Input(2, 3, 4) + * | + * Shape + * | + * Output(3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4d, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 9.817013, -10.169584, -11.175514, 6.3500366, -39.949837, 2.3447914, 14.254675, 20.6128, + 8.819141, -10.237312, -5.171467, 4.7246437, 11.657671, 20.094395, 11.213078, -13.8377495, + 10.846771, -15.841316, 7.4385757, -6.9196777, 12.076214, 18.011564, -14.684473, 2.7402115}; + +const std::vector reference_output_data = {2, 3, 4}; +} // namespace shape_kernel + +template class TestDataShapeKernel : public TestDataBase +{ +public: + TestDataShapeKernel() + { + _input_data = shape_kernel::input_data; + _reference_output_data = shape_kernel::reference_output_data; + _test_kernel_model_circle = shape_kernel::test_kernel_model_circle; + } + + ~TestDataShapeKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/FloatSliceKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/FloatSliceKernel.h new file mode 100644 index 0000000..6586d37 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/FloatSliceKernel.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_SLICE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_SLICE_KERNEL_H + +#include "TestDataSliceBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace slice_float +{ +/* + * Slice Kernel: + * + * Input(3, 2, 3) + * | + * Slice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x7c, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-6.0019245, -25.824707, -46.067307, -17.168013, -9.692509, + -42.846222, -18.903988, -26.145718, -10.458343, -27.042469, + -34.02651, -43.133247, 0.57390976, 9.837246, -22.825436, + 9.404066, -16.980595, -16.267637}; + +const std::vector reference_output_data = {-18.903988, -26.145718, -10.458343}; + +} // namespace slice_float + +class TestDataFloatSlice : public TestDataSliceBase +{ +public: + TestDataFloatSlice() + { + _input_data = slice_float::input_data; + _reference_output_data = slice_float::reference_output_data; + _test_kernel_model_circle = slice_float::test_kernel_model_circle; + } + + ~TestDataFloatSlice() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_SLICE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/NegSliceKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/NegSliceKernel.h new file mode 100644 index 0000000..2b59e8e --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/NegSliceKernel.h @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_SLICE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_SLICE_KERNEL_H + +#include "TestDataSliceBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace slice_type_mismatch +{ +/* + * Slice Kernel with input type != output_type: + * + * Input(3, 2, 3) - Float32 + * | + * Slice + * | + * Output(1, 1, 3) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0xe8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xac, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, + 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace slice_type_mismatch + +namespace slice_wrong_begin_type +{ +/* + * Slice Kernel with wrong begin type (should be int32 or int64): + * + * Input(3, 2, 3) Begin(3) - Float32 + * | / + * Slice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xdc, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace slice_wrong_begin_type + +namespace slice_wrong_size_type +{ +/* + * Slice Kernel with wrong size type (should be int32 or int64): + * + * Input(3, 2, 3) Size(3) - Float32 + * | / + * Slice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa8, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace slice_wrong_size_type + +namespace slice_wrong_input_shape +{ +/* + * Slice Kernel with wrong input shape (rank is 6 but should be <= 5): + * + * Input(3, 2, 3, 1, 1, 1) + * | + * Slice + * | + * Output(1, 1, 3, 1, 1, 1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, + 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x70, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +} // namespace slice_wrong_input_shape + +class TestDataTypeMismatchSlice : public NegTestDataBase +{ +public: + TestDataTypeMismatchSlice() + { + _test_kernel_model_circle = slice_type_mismatch::test_kernel_model_circle; + } + + ~TestDataTypeMismatchSlice() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class TestDataWrongBeginTypeSlice : public NegTestDataBase +{ +public: + TestDataWrongBeginTypeSlice() + { + _test_kernel_model_circle = slice_wrong_begin_type::test_kernel_model_circle; + } + + ~TestDataWrongBeginTypeSlice() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class TestDataWrongSizeTypeSlice : public NegTestDataBase +{ +public: + TestDataWrongSizeTypeSlice() + { + _test_kernel_model_circle = slice_wrong_size_type::test_kernel_model_circle; + } + + ~TestDataWrongSizeTypeSlice() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class TestDataWrongInputShapeSlice : public NegTestDataBase +{ +public: + TestDataWrongInputShapeSlice() + { + _test_kernel_model_circle = slice_wrong_input_shape::test_kernel_model_circle; + } + + ~TestDataWrongInputShapeSlice() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_SLICE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantS16SliceKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantS16SliceKernel.h new file mode 100644 index 0000000..7d3d5a7 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantS16SliceKernel.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_QUANT_S16_SLICE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_QUANT_S16_SLICE_KERNEL_H + +#include "TestDataSliceBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace slice_int16 +{ +/* + * Slice Kernel: + * + * Input(3, 2, 3) + * | + * Slice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, + 0x2c, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xba, 0xc0, 0x40, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x3f, 0xbf, 0x40, + 0x01, 0x00, 0x00, 0x00, 0xc2, 0xc0, 0xc0, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, + 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x50, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xba, 0xc0, 0x40, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x3f, 0xbf, 0x40, + 0x01, 0x00, 0x00, 0x00, 0xc2, 0xc0, 0xc0, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-24, -47, -23, -26, -23, -25, -24, -52, -14, + -23, -23, -23, -41, -24, -26, -22, -53, -23}; + +const std::vector reference_output_data = {-24, -52, -14}; + +} // namespace slice_int16 + +class TestDataS16Slice : public TestDataSliceBase +{ +public: + TestDataS16Slice() + { + _input_data = slice_int16::input_data; + _reference_output_data = slice_int16::reference_output_data; + _test_kernel_model_circle = slice_int16::test_kernel_model_circle; + } + + ~TestDataS16Slice() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_QUANT_S16_SLICE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantU8SliceKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantU8SliceKernel.h new file mode 100644 index 0000000..1f688d2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/QuantU8SliceKernel.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_QUANT_U8_SLICE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_QUANT_U8_SLICE_KERNEL_H + +#include "TestDataSliceBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace slice_uint8 +{ +/* + * Slice Kernel: + * + * Input(3, 2, 3) + * | + * Slice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3a, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x44, 0x00, 0x00, 0x00, + 0x2c, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xba, 0xc0, 0x40, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x3f, 0xbf, 0x40, + 0x01, 0x00, 0x00, 0x00, 0xc2, 0xc0, 0xc0, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x65, 0x67, 0x69, + 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x50, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xba, 0xc0, 0x40, 0x3d, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x3f, 0xbf, 0x40, + 0x01, 0x00, 0x00, 0x00, 0xc2, 0xc0, 0xc0, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {3, 218, 233, 232, 234, 242, 238, 233, 218, + 240, 1, 233, 225, 242, 218, 249, 203, 225}; + +const std::vector reference_output_data = {238, 233, 218}; + +} // namespace slice_uint8 + +class TestDataU8Slice : public TestDataSliceBase +{ +public: + TestDataU8Slice() + { + _input_data = slice_uint8::input_data; + _reference_output_data = slice_uint8::reference_output_data; + _test_kernel_model_circle = slice_uint8::test_kernel_model_circle; + } + + ~TestDataU8Slice() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_QUANT_U8_SLICE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/TestDataSliceBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/TestDataSliceBase.h new file mode 100644 index 0000000..759a248 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/slice/TestDataSliceBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SLICE_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_SLICE_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataSliceBase : public TestDataBase +{ +public: + TestDataSliceBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SLICE_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/FloatSplitKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/FloatSplitKernel.h new file mode 100644 index 0000000..7a98627 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/FloatSplitKernel.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_FLOAT_H + +#include "TestDataSplitBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace split_float +{ + +/* + * Split Kernel: + * + * Input(6, 1, 2) Split_dim(scalar=0) + * \ | / + * Split + * / \ + * Output(3, 1, 2) Output(3, 1, 2) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x23, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa8, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x74, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, 0x70, 0x6c, 0x69, + 0x74, 0x5f, 0x64, 0x69, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {20.07405, 30.467144, 34.943245, 38.18743, + 39.956573, 30.283304, 42.44296, 41.62288, + 16.24289, 38.450634, 27.079258, 47.636314}; + +const std::vector reference_output_data_1 = {20.07405, 30.467144, 34.943245, + 38.18743, 39.956573, 30.283304}; +const std::vector reference_output_data_2 = {42.44296, 41.62288, 16.24289, + 38.450634, 27.079258, 47.636314}; + +} // namespace split_float + +class TestDataFloatSplit : public TestDataSplitBase +{ +public: + TestDataFloatSplit() + { + _input_data = split_float::input_data; + _reference_output_data_1 = split_float::reference_output_data_1; + _reference_output_data_2 = split_float::reference_output_data_2; + _test_kernel_model_circle = split_float::test_kernel_model_circle; + } + + ~TestDataFloatSplit() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/IntSplitKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/IntSplitKernel.h new file mode 100644 index 0000000..87b898d --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/IntSplitKernel.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_INT_H + +#include "TestDataSplitBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace split_int +{ + +/* + * Split Kernel: + * + * Input(6, 1, 2) Split_dim(scalar=0) + * \ | / + * Split + * / \ + * Output(3, 1, 2) Output(3, 1, 2) + */ + +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x23, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa4, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x78, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xa8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x64, 0x69, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {40, 40, 30, 40, 35, 25, 35, 30, 35, 30, 40, 35}; + +const std::vector reference_output_data_1 = {40, 40, 30, 40, 35, 25}; +const std::vector reference_output_data_2 = {35, 30, 35, 30, 40, 35}; + +} // namespace split_int + +class TestDataIntSplit : public TestDataSplitBase +{ +public: + TestDataIntSplit() + { + _input_data = split_int::input_data; + _reference_output_data_1 = split_int::reference_output_data_1; + _reference_output_data_2 = split_int::reference_output_data_2; + _test_kernel_model_circle = split_int::test_kernel_model_circle; + } + + ~TestDataIntSplit() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/TestDataSplitBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/TestDataSplitBase.h new file mode 100644 index 0000000..7ddcd75 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split/TestDataSplitBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataSplitBase : public TestDataBase +{ +public: + TestDataSplitBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _reference_output_data_1; + case 1: + return _reference_output_data_2; + default: + assert(false && "Wrong input index"); + } + } + +protected: + std::vector _input_data; + std::vector _reference_output_data_1; + std::vector _reference_output_data_2; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SPLIT_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split_v/SplitVKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split_v/SplitVKernel.h new file mode 100644 index 0000000..bb87c40 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/split_v/SplitVKernel.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SPLIT_V_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_SPLIT_V_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace split_v_kernel +{ +/* + * SplitV Kernel: + * + * Input(6, 1, 2) Size_splits([1, 2, 3]) Split_dim(scalar=0) + * \ | / + * SplitV + * / | \ + * Output(1, 1, 2) Output(2, 1, 2) Output(3, 1, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x56, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x74, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xd8, 0xff, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x64, 0x69, 0x6d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x70, 0x6c, + 0x69, 0x74, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-3.9030151, -4.558613, -12.806297, -2.64188, + 17.035677, 26.150639, -12.618465, 0.8286438, + -4.850197, -0.20810127, 3.8918018, 4.1862106}; + +const std::vector reference_output_data_1 = {-3.9030151, -4.558613}; +const std::vector reference_output_data_2 = {-12.806297, -2.64188, 17.035677, 26.150639}; +const std::vector reference_output_data_3 = {-12.618465, 0.8286438, -4.850197, + -0.20810127, 3.8918018, 4.1862106}; + +} // namespace split_v_kernel + +template class TestDataSplitVKernel : public TestDataBase +{ +public: + TestDataSplitVKernel() + { + _input_data = split_v_kernel::input_data; + _reference_output_data_1 = split_v_kernel::reference_output_data_1; + _reference_output_data_2 = split_v_kernel::reference_output_data_2; + _reference_output_data_3 = split_v_kernel::reference_output_data_3; + _test_kernel_model_circle = split_v_kernel::test_kernel_model_circle; + } + + ~TestDataSplitVKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _reference_output_data_1; + case 1: + return _reference_output_data_2; + case 2: + return _reference_output_data_3; + default: + assert(false && "Wrong input index"); + } + } + +protected: + std::vector _input_data; + std::vector _reference_output_data_1; + std::vector _reference_output_data_2; + std::vector _reference_output_data_3; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SPLIT_V_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/strided_slice/StridedSliceKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/strided_slice/StridedSliceKernel.h new file mode 100644 index 0000000..4d69013 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/strided_slice/StridedSliceKernel.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_STRIDED_SLICE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_STRIDED_SLICE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace strided_slice_kernel +{ +/* + * StridedSlice Kernel: + * + * Input(3, 2, 3) Begin([1, 0, 0]) End([2, 1, 3]) Strides([1, 1, 1]) + * | | | | + * ------------------- | | ------------------ + * \ | | / + * StridedSlice + * | + * Output(1, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xce, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xe6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, + 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x58, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xb4, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x73, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x65, 0x6e, 0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x62, 0x65, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2d, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {15.78072, -20.820415, 17.688091, 12.961567, -5.469105, + -7.867565, -6.989258, -9.068207, 4.974188, 6.2882156, + 7.269455, 6.161186, 15.821367, -17.094833, 24.529251, + 1.1271019, -8.563269, -7.494442}; + +const std::vector reference_output_data = {-6.989258, -9.068207, 4.974188}; + +} // namespace strided_slice_kernel + +template class TestDataStridedSliceKernel : public TestDataBase +{ +public: + TestDataStridedSliceKernel() + { + _input_data = strided_slice_kernel::input_data; + _reference_output_data = strided_slice_kernel::reference_output_data; + _test_kernel_model_circle = strided_slice_kernel::test_kernel_model_circle; + } + + ~TestDataStridedSliceKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_STRIDED_SLICE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/FloatSubKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/FloatSubKernel.h new file mode 100644 index 0000000..25f8386 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/FloatSubKernel.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_FLOAT_H +#define LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_FLOAT_H + +#include "TestDataSubBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace sub_float_with_broadcasting +{ + +/* + * Sub Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Sub(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x53, 0x75, 0x62, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {-8.326889, -1.022953, 0.1884613, 22.581654, 40.9168, + 36.762, -9.715984, 4.0496464, 31.648043, 11.501019}; +const std::vector input2_data = {15.127094, 3.3150635}; +const std::vector reference_output_data = {-23.453983, -16.150047, -14.938633, 7.4545593, + 25.789707, 33.446938, -13.031048, 0.7345829, + 28.33298, 8.185955}; + +} // namespace sub_float_with_broadcasting + +namespace sub_float_no_broadcasting +{ +/* + * Sub Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Sub(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xfe, 0xff, 0xff, 0xc8, 0xfe, 0xff, 0xff, 0xcc, 0xfe, 0xff, 0xff, 0xd0, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8c, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x53, 0x75, 0x62, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xc6, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {-12.091457, 7.402893, 5.7398167, 1.6491795, 12.5600815, + 13.821102, 8.744585, 11.44549, -0.7579155, 4.1162605}; +std::vector input2_data = {13.1849, 12.109516, -0.026350021, -5.5562515, -3.3528423, + 10.397262, 25.990755, 19.270943, -26.921743, 3.6311188}; +std::vector reference_output_data = {-25.276358, -4.706623, 5.7661667, 7.205431, + 15.912924, 3.4238405, -17.24617, -7.825453, + 26.163828, 0.48514175}; + +} // namespace sub_float_no_broadcasting + +class TestDataFloatSub : public TestDataSubBase +{ +public: + explicit TestDataFloatSub(bool is_with_broadcast) : TestDataSubBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = sub_float_with_broadcasting::input1_data; + _input2_data = sub_float_with_broadcasting::input2_data; + _reference_output_data = sub_float_with_broadcasting::reference_output_data; + _test_kernel_model_circle = sub_float_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = sub_float_no_broadcasting::input1_data; + _input2_data = sub_float_no_broadcasting::input2_data; + _reference_output_data = sub_float_no_broadcasting::reference_output_data; + _test_kernel_model_circle = sub_float_no_broadcasting::test_kernel_model_circle; + } + } + + ~TestDataFloatSub() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_FLOAT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/IntSubKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/IntSubKernel.h new file mode 100644 index 0000000..81d0edc --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/IntSubKernel.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_INT_H +#define LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_INT_H + +#include "TestDataSubBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace sub_int_with_broadcasting +{ + +/* + * Sub Kernel: + * + * Input_1(2, 5) Input_2(2, 1) + * \ / + * Sub(with broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x53, 0x75, 0x62, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +const std::vector input1_data = {13, 23, 5, -3, 15, 5, 5, 5, 13, 12}; +const std::vector input2_data = {5, 13}; +const std::vector reference_output_data = {8, 18, 0, -8, 10, -8, -8, -8, 0, -1}; + +} // namespace sub_int_with_broadcasting + +namespace sub_int_no_broadcasting +{ +/* + * Sub Kernel: + * + * Input_1(2, 5) Input_2(2, 5) + * \ / + * Sub(no broadcast) + * | + * Output(2, 5) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x36, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0xfe, 0xff, 0xff, 0xbc, 0xfe, 0xff, 0xff, 0xc0, 0xfe, 0xff, 0xff, 0xc4, 0xfe, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x92, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x53, 0x75, 0x62, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xc2, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, + 0x0d, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; + +std::vector input1_data = {5, 7, 13, -3, 13, 7, -5, 5, 13, 5}; +std::vector input2_data = {-5, -11, 5, 5, 14, 5, 42, 6, 5, 15}; +std::vector reference_output_data = {10, 18, 8, -8, -1, 2, -47, -1, 8, -10}; + +} // namespace sub_int_no_broadcasting + +class TestDataIntSub : public TestDataSubBase +{ +public: + explicit TestDataIntSub(bool is_with_broadcast) : TestDataSubBase(is_with_broadcast) + { + if (is_with_broadcast) + { + _input1_data = sub_int_with_broadcasting::input1_data; + _input2_data = sub_int_with_broadcasting::input2_data; + _reference_output_data = sub_int_with_broadcasting::reference_output_data; + _test_kernel_model_circle = sub_int_with_broadcasting::test_kernel_model_circle; + } + else + { + _input1_data = sub_int_no_broadcasting::input1_data; + _input2_data = sub_int_no_broadcasting::input2_data; + _reference_output_data = sub_int_no_broadcasting::reference_output_data; + _test_kernel_model_circle = sub_int_no_broadcasting::test_kernel_model_circle; + } + } + + ~TestDataIntSub() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_INT_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/NegSubKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/NegSubKernel.h new file mode 100644 index 0000000..d5ea240 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/NegSubKernel.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_SUB_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_SUB_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_inputs_type_mismatch_sub_kernel +{ +/* + * Sub Kernel with inputs type mismatch: + * + * Input_1(1, 5, 2, 3)-Int Input_2(1, 5, 2, 3)-Float + * \ / + * Sub(no broadcast) + * | + * Output(1, 5, 2, 3)-Float + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_inputs_type_mismatch_sub_kernel + +namespace neg_input_output_type_mismatch_sub_kernel +{ +/* + * Sub Kernel with input output types mismatch: + * + * Input_1(1, 5, 2, 3)-Float Input_2(1, 5, 2, 3)-Float + * \ / + * Sub(no broadcast) + * | + * Output(1, 5, 2, 3)-Int + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_sub_kernel + +namespace neg_no_quant_params_sub_kernel +{ +/* + * Sub S16 Kernel without quant params: + * + * Input_1(1, 5, 2, 3)-Int16 Input_2(1, 5, 2, 3)-Int16 + * \ / + * Sub(no broadcast, no quant params) + * | + * Output(1, 5, 2, 3)-Int16 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x88, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x10, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xcc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_no_quant_params_sub_kernel + +class NegTestDataInputsTypeMismatchSubKernel : public NegTestDataBase +{ +public: + NegTestDataInputsTypeMismatchSubKernel() + { + _test_kernel_model_circle = neg_inputs_type_mismatch_sub_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputsTypeMismatchSubKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataInputOutputTypeMismatchSubKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchSubKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_sub_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchSubKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +class NegTestDataNoQuantParamsSubKernel : public NegTestDataBase +{ +public: + NegTestDataNoQuantParamsSubKernel() + { + _test_kernel_model_circle = neg_no_quant_params_sub_kernel::test_kernel_model_circle; + } + + ~NegTestDataNoQuantParamsSubKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_SUB_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/TestDataSubBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/TestDataSubBase.h new file mode 100644 index 0000000..8c0775e --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/sub/TestDataSubBase.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataSubBase : public TestDataBase +{ +public: + explicit TestDataSubBase(bool) + { + // Do nothing + } + + TestDataSubBase() = delete; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input1_data; + case 1: + return _input2_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input1_data; + std::vector _input2_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_SUB_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/FloatTanhKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/FloatTanhKernel.h new file mode 100644 index 0000000..82ce797 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/FloatTanhKernel.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_TANH_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_TANH_KERNEL_H + +#include "TestDataTanhBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace tanh_float +{ +/* + * Tanh Kernel: + * + * Input(1, 3, 3, 2) + * | + * Tanh + * | + * Output(1, 3, 3, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {-18.051472, -25.18281, -1.566864, 10.271992, 9.358999, + 30.315918, 22.305614, 4.2511578, 5.960436, -8.210682, + 21.21092, -18.009472, -13.981232, -3.081173, 7.770035, + 7.670355, 21.851545, 17.201824}; + +const std::vector reference_output_data = { + -1.0, -1.0, -0.9165255, 1.0, 1.0, 1.0, 1.0, 0.99959403, 0.99998677, + -1.0, 1.0, -1.0, -1.0, -0.9957943, 0.9999997, 0.99999964, 1.0, 1.0}; + +} // namespace tanh_float + +class TestDataFloatTanh : public TestDataTanhBase +{ +public: + TestDataFloatTanh() + { + _input_data = tanh_float::input_data; + _reference_output_data = tanh_float::reference_output_data; + _test_kernel_model_circle = tanh_float::test_kernel_model_circle; + } + + ~TestDataFloatTanh() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_TANH_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/NegTanhKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/NegTanhKernel.h new file mode 100644 index 0000000..3ff0b68 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/NegTanhKernel.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_TANH_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_TANH_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_kernel +{ +/* + * Tanh Kernel with input output type mismatch: + * + * Input(1, 3, 3, 2) - Float32 + * | + * Tanh + * | + * Output(1, 3, 3, 2) - Int32 + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x69, 0x66, 0x6d, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_kernel + +class NegTestDataInputOutputTypeMismatchTanhKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchTanhKernel() + { + _test_kernel_model_circle = neg_input_output_type_mismatch_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchTanhKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_TANH_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/TestDataTanhBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/TestDataTanhBase.h new file mode 100644 index 0000000..2e12dfe --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/tanh/TestDataTanhBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_TANH_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_TANH_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataTanhBase : public TestDataBase +{ +public: + TestDataTanhBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_TANH_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/transpose/TransposeKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/transpose/TransposeKernel.h new file mode 100644 index 0000000..ebeda65 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/transpose/TransposeKernel.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_TRANSPOSE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_TRANSPOSE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace transpose_kernel +{ +/* + * Transpose Kernel: + * + * Input(3, 8, 1) Perm([1, 2, 0]) + * \ / + * Transpose + * | + * Output(8, 1, 3) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x65, 0x72, 0x6d, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + 20.761623, 13.150787, 7.0800495, -5.8079357, -4.265215, -2.801073, 14.42078, 8.526264, + 27.862984, 1.067873, 19.894545, 0.25564194, 10.414932, 3.317482, -0.727417, -5.702162, + -2.8192825, 19.296608, 23.116634, 6.5216866, -7.0733185, -3.3730087, -34.845665, 28.050354}; + +const std::vector reference_output_data = { + 20.761623, 27.862984, -2.8192825, 13.150787, 1.067873, 19.296608, 7.0800495, 19.894545, + 23.116634, -5.8079357, 0.25564194, 6.5216866, -4.265215, 10.414932, -7.0733185, -2.801073, + 3.317482, -3.3730087, 14.42078, -0.727417, -34.845665, 8.526264, -5.702162, 28.050354}; +} // namespace transpose_kernel + +template class TestDataTransposeKernel : public TestDataBase +{ +public: + TestDataTransposeKernel() + { + _input_data = transpose_kernel::input_data; + _reference_output_data = transpose_kernel::reference_output_data; + _test_kernel_model_circle = transpose_kernel::test_kernel_model_circle; + } + + ~TestDataTransposeKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_TRANSPOSE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/FloatUnidirectionalLSTMKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/FloatUnidirectionalLSTMKernel.h new file mode 100644 index 0000000..ea96748 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/FloatUnidirectionalLSTMKernel.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_FLOAT_UNIDIRECTIONAL_LSTM_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_UNIDIRECTIONAL_LSTM_KERNEL_H + +#include "TestDataUnidirectionalLSTMBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace unidir_lstm_float +{ +/* + * UnidirectionalLSTM Kernel: + * + * Input(1, 4, 4) + * | + * UnidirectionalLSTM + * | + * Output(1, 4, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xb4, 0x01, 0x00, 0x00, 0xd0, 0x05, 0x00, 0x00, 0xec, 0x05, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0xa0, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, + 0x64, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0xe4, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xfe, 0xff, 0xff, + 0xd2, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x84, 0x9b, 0x3c, 0xbe, + 0x48, 0xfc, 0x22, 0xbf, 0x35, 0x43, 0xba, 0x3e, 0x18, 0x5c, 0xdb, 0x3e, 0x4b, 0x05, 0xdd, 0xbe, + 0xf5, 0x0f, 0x1e, 0xbf, 0x1f, 0x2e, 0x09, 0x3f, 0x9e, 0xb5, 0x2f, 0x3f, 0xfe, 0xfe, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xca, 0xfe, 0x05, 0x3f, 0xea, 0x91, 0x06, 0xbe, + 0x77, 0xf4, 0xa7, 0xbe, 0x3f, 0x02, 0x23, 0xbf, 0xd1, 0xdc, 0x94, 0xbd, 0xc2, 0xdd, 0xb1, 0xbe, + 0x45, 0x13, 0xc8, 0x3e, 0x7f, 0x6b, 0xef, 0x3e, 0x2a, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0xec, 0xde, 0xe2, 0xbe, 0xf6, 0x46, 0x01, 0xbf, 0xed, 0x4d, 0x97, 0xbd, + 0xf2, 0xed, 0x09, 0xbf, 0x88, 0x4c, 0xe1, 0x3e, 0x60, 0x74, 0x89, 0x3e, 0x29, 0x79, 0x75, 0x3c, + 0x9b, 0x8f, 0xdb, 0xbe, 0x56, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0xe7, 0xc8, 0x6a, 0x3e, 0x16, 0x06, 0x8b, 0xbd, 0x49, 0xf5, 0xe5, 0x3e, 0x01, 0xfb, 0xf0, 0x3e, + 0x7c, 0x48, 0x10, 0xbf, 0x12, 0xd8, 0x94, 0xbe, 0x9a, 0xec, 0xaf, 0x3e, 0x4c, 0x1a, 0xdb, 0xbe, + 0x82, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, + 0x00, 0x00, 0x80, 0x3f, 0x96, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x8d, 0xd4, 0xdb, 0xbd, 0xfe, 0x63, 0xd1, 0x3e, 0x9f, 0x60, 0x1a, 0x3d, + 0xa1, 0x48, 0x0b, 0xbf, 0xc6, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x3a, 0xb2, 0xca, 0x3e, 0x58, 0x1a, 0x04, 0xbf, 0xe6, 0x76, 0x9f, 0x3e, 0x61, 0xa7, 0xd8, 0x3e, + 0xe2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xf1, 0x48, 0x5c, 0xbe, + 0xcd, 0x54, 0xad, 0x3c, 0x9f, 0x8e, 0xbf, 0x3e, 0x69, 0xac, 0xfd, 0x3d, 0x00, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x93, 0x70, 0x21, 0xbf, 0x76, 0x27, 0x8e, 0x3c, 0x97, 0xe3, 0xc5, 0x3e, 0xe5, 0x7d, 0x8c, 0x3e, + 0xf4, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, + 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x04, + 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0xe4, 0x02, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0xec, 0x01, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0xfd, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x66, 0x75, 0x6c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, + 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xec, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x0c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x6c, 0x73, 0x74, 0x6d, 0x2f, + 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x31, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xdc, 0xfd, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x39, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x3c, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x37, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x36, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x9c, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x35, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc8, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x34, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xf4, 0xfe, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x33, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x32, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x31, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x6c, + 0x73, 0x74, 0x6d, 0x2f, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x31, 0x3a, + 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, + 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -3.509163, -18.256927, 6.4799614, 10.296598, 30.371328, 18.692572, 10.12867, -26.44944, + 25.324795, 3.8303719, 20.93112, 22.603086, -4.308655, 2.3276749, -5.9565907, 25.611776}; + +const std::vector reference_output_data = {0.7613201, -0.7570043, 0.0480366, + -4.3364323e-11, -0.7613433, 1.3437739e-08, + -0.7613537, -7.000451e-08}; + +} // namespace unidir_lstm_float + +class TestDataFloatUnidirectionalLSTM : public TestDataUnidirectionalLSTMBase +{ +public: + TestDataFloatUnidirectionalLSTM() + { + _input_data = unidir_lstm_float::input_data; + _reference_output_data = unidir_lstm_float::reference_output_data; + _test_kernel_model_circle = unidir_lstm_float::test_kernel_model_circle; + } + + ~TestDataFloatUnidirectionalLSTM() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_UNIDIRECTIONAL_LSTM_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/QuantS8UnidirectionalLSTM.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/QuantS8UnidirectionalLSTM.h new file mode 100644 index 0000000..d4bd798 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/QuantS8UnidirectionalLSTM.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_QUANT_S8_UNIDIRECTIONAL_LSTM_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_QUANT_S8_UNIDIRECTIONAL_LSTM_KERNEL_H + +#include "TestDataUnidirectionalLSTMBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace unidir_lstm_int8 +{ +/* + * UnidirectionalLSTM Kernel: + * + * Input(1, 20, 20) + * | + * UnidirectionalLSTM + * | + * Output(1, 20, 2) + */ +const unsigned char test_kernel_model_circle[] = { + 0x1c, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0xd8, 0x0a, 0x00, 0x00, 0xf4, 0x0a, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x00, + 0xfc, 0x01, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0xec, 0x01, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, + 0xc4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, + 0x2c, 0x01, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2e, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x31, + 0x2e, 0x30, 0x00, 0x00, 0x92, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x2e, 0x31, 0x33, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0xfe, 0xff, 0xff, 0xb2, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xff, 0x7f, 0xff, 0x7f, 0xc2, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd2, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x7f, 0xdb, 0x47, 0xd4, 0x00, 0xba, 0x1c, 0x49, 0xd9, 0xb1, 0x98, 0xa7, 0x76, 0x4d, 0x90, 0xab, + 0x19, 0x5d, 0x72, 0xfa, 0xe1, 0x95, 0x07, 0x05, 0x63, 0xfb, 0x6e, 0x59, 0xd9, 0x0e, 0x94, 0x04, + 0x01, 0x72, 0x3b, 0x8e, 0x58, 0x4a, 0xd1, 0xd6, 0x06, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xbd, 0x21, 0x0b, 0x84, 0xf4, 0xc6, 0x67, 0x3f, 0x19, 0xc2, 0x5f, 0x92, + 0x19, 0x4a, 0x9a, 0xef, 0xbb, 0x79, 0x45, 0x93, 0x09, 0x17, 0x50, 0xdd, 0x2e, 0x5c, 0xe3, 0x5f, + 0xc9, 0x81, 0xb9, 0x1a, 0x5e, 0x45, 0x84, 0xe1, 0xb4, 0xe3, 0x58, 0x1b, 0x3a, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x03, 0xa1, 0x30, 0x2e, 0x08, 0x1d, 0x39, 0xe1, + 0xed, 0xea, 0x19, 0xc0, 0x86, 0xe4, 0x71, 0x50, 0xc6, 0x4f, 0x05, 0xbc, 0xf7, 0xdf, 0x93, 0xea, + 0x94, 0xbd, 0x00, 0x49, 0x2a, 0x2a, 0xd0, 0x5e, 0x06, 0x81, 0xdd, 0xe0, 0xaf, 0xf9, 0x71, 0x24, + 0x6e, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x9b, 0xf8, 0x5a, 0xaa, + 0x6c, 0x6a, 0x2f, 0x8a, 0x9f, 0xe1, 0x7e, 0x85, 0x81, 0xe1, 0xea, 0x6d, 0x89, 0x28, 0xd1, 0x49, + 0x82, 0x6b, 0x25, 0x48, 0x1a, 0xc6, 0xce, 0x0b, 0xcd, 0xd2, 0x24, 0x51, 0xf3, 0x48, 0xf6, 0x6f, + 0xe2, 0x5c, 0x36, 0x5b, 0xa2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7f, 0xd4, 0xbf, 0xc1, 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf6, 0x10, 0x7f, 0x09, 0xc2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf4, 0x81, 0x11, 0x1f, 0xd2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7f, 0x5f, 0x7e, 0x84, 0xc4, 0xff, 0xff, 0xff, 0xc8, 0xff, 0xff, 0xff, 0xea, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, + 0xc8, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x18, 0x07, 0x00, 0x00, 0xb0, 0x06, 0x00, 0x00, 0x54, 0x06, 0x00, 0x00, + 0xf8, 0x05, 0x00, 0x00, 0x9c, 0x05, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0xe4, 0x04, 0x00, 0x00, + 0x88, 0x04, 0x00, 0x00, 0x2c, 0x04, 0x00, 0x00, 0xd0, 0x03, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, + 0x18, 0x03, 0x00, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, + 0xac, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4a, 0xf9, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x48, 0x00, 0x00, 0x00, + 0x3c, 0xf9, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1d, 0x87, 0xfd, 0x3b, 0x19, 0x00, 0x00, 0x00, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, + 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x00, 0xb4, 0xf9, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1d, 0x87, 0xfd, 0x3b, 0x23, 0x00, 0x00, 0x00, + 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, + 0x61, 0x74, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, + 0x08, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x94, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x65, 0x6c, + 0x6c, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, + 0x5f, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x00, 0x04, 0xfb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x12, 0x00, 0x00, 0x00, + 0x74, 0x66, 0x6c, 0x2e, 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x5f, 0x71, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x31, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x00, 0x74, 0xfb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1d, 0x87, 0xfd, 0x3b, 0x11, 0x00, 0x00, 0x00, + 0x74, 0x66, 0x6c, 0x2e, 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x5f, 0x71, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xe2, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, 0xd4, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x67, 0x21, 0x6d, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x39, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, + 0x2c, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xec, 0x45, 0x69, 0x3b, + 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x92, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, 0x84, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xb9, 0xbc, 0x68, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x37, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xea, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, + 0xdc, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3f, 0xac, 0x6e, 0x3b, + 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x36, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x42, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, 0x34, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x24, 0x56, 0x7e, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x33, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9a, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, + 0x8c, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0xa6, 0x65, 0x3b, + 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xf2, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, 0xe4, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x38, 0x10, 0xb8, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4a, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x38, 0x00, 0x00, 0x00, + 0x3c, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x7d, 0x3b, + 0x0e, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xa2, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x94, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x38, 0xa5, 0x3a, 0x10, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, + 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x34, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfa, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x00, + 0xec, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x1c, 0xa1, 0x3a, + 0x10, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x34, 0x31, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x52, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x44, 0xff, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb4, 0x26, 0xa4, 0x3a, 0x0f, 0x00, 0x00, 0x00, + 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x34, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x00, + 0x9c, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x7b, 0xa1, 0x3a, + 0x0f, 0x00, 0x00, 0x00, 0x61, 0x72, 0x69, 0x74, 0x68, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x35, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x50, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xa3, 0x36, 0xb1, 0x3e, + 0x19, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x31, 0x3a, 0x30, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, + 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, + 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = { + -19, -32, -10, -8, -23, -21, -19, -8, -10, -19, -35, -28, -19, -19, -8, -17, -17, -19, 5, + -19, -30, -21, -30, -8, -28, -37, -30, -17, -10, -28, -30, -17, -19, -21, -19, -19, 12, -6, + -10, -8, -10, -8, -21, -19, -30, -19, -8, -6, -24, -17, -8, -8, -19, -10, -28, -48, -10, + -19, -19, -17, 1, -30, -21, -17, 12, -19, -1, -19, -17, -1, -30, -19, -19, -28, -1, -10, + -32, -19, -19, -21, -19, -39, -8, -6, -12, -21, -28, -19, -15, -26, -19, -17, -19, -19, -19, + -19, -28, -19, -17, -30, 3, -10, -28, -30, -10, -19, -19, -21, -8, -28, -19, -19, -28, -17, + -19, -19, -19, -17, -17, -8, -12, -19, -19, -19, -30, -19, -8, -10, -28, -19, -30, -28, -26, + -8, -19, -19, -10, -30, -30, -39, -21, -39, -19, 1, -8, -19, -21, -10, -8, -10, -30, -30, + -19, -30, -19, -21, -19, -19, -21, -41, -17, -41, -19, -10, -19, -10, -30, -19, 3, -6, -23, + -28, -23, -10, -19, -10, -19, 1, -19, -30, -10, -8, -17, -19, -17, -26, -19, -19, -19, -26, + -23, -28, -19, -28, -12, -19, -30, -19, -19, -17, -28, -19, -28, -30, -8, -15, -1, -8, -19, + -19, -19, -21, -17, -8, -19, -21, -28, -30, 3, -28, -19, -19, -10, -19, -6, -28, -19, -17, + -46, -19, -19, -19, -10, 3, -19, -19, -19, -19, -19, -17, -21, -32, -19, -30, -19, -19, -19, + -17, -24, -10, -28, -8, -32, -10, -26, -8, -30, -17, -19, -17, -17, -8, -19, 3, -1, -30, + -19, -15, -19, -37, -19, -19, -19, -19, -19, -21, -8, -21, -19, -50, -30, -10, -6, -19, -19, + -24, -21, -17, -10, -17, -19, -19, -19, -17, -15, -21, -50, -19, -1, -10, -19, -19, -19, -39, + -28, -17, 10, -19, 1, 1, -8, -15, -19, -23, -10, -8, -19, 3, -19, 3, -19, -30, -23, + -48, -8, -8, -28, -10, -37, -19, -15, -19, -26, -10, -1, -10, -19, -19, -32, -17, -30, -17, + -21, -32, -19, 5, -21, -12, -1, -19, -10, -30, -19, -41, -17, -23, -10, -26, -28, -30, -30, + -21, -19, -10, -10, -19, -8, -12, -19, -19, 1, -19, -1, -10, -19, -56, -21, -10, -41, -17, + -28, -10, -25, -30, -21, -48, -30, -30, -21, -19, -19, -19, -30, -8, 12, -10, -8, -6, -28, + -17}; + +const std::vector reference_output_data = { + 0, 85, 0, 98, 0, 66, 29, -7, 13, -95, -5, -90, 0, -7, 0, -4, 4, -74, 32, -96, + 21, -86, 7, -98, 48, -89, 0, -98, 90, -97, 96, 82, 46, -35, 32, -1, -2, -85, -2, -97}; + +} // namespace unidir_lstm_int8 + +class TestDataInt8UnidirectionalLSTM : public TestDataUnidirectionalLSTMBase +{ +public: + TestDataInt8UnidirectionalLSTM() + { + _input_data = unidir_lstm_int8::input_data; + _reference_output_data = unidir_lstm_int8::reference_output_data; + _test_kernel_model_circle = unidir_lstm_int8::test_kernel_model_circle; + } + + ~TestDataInt8UnidirectionalLSTM() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_QUANT_S8_UNIDIRECTIONAL_LSTM_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/TestDataUnidirectionalLSTMBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/TestDataUnidirectionalLSTMBase.h new file mode 100644 index 0000000..9422655 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/unidirectional_lstm/TestDataUnidirectionalLSTMBase.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_UNIDIRECTIONAL_LSTM_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_UNIDIRECTIONAL_LSTM_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataUnidirectionalLSTMBase : public TestDataBase +{ +public: + TestDataUnidirectionalLSTMBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_UNIDIRECTIONAL_LSTM_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/NegWhileKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/NegWhileKernel.h new file mode 100644 index 0000000..d193fbf --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/NegWhileKernel.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_NEG_WHILE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_WHILE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace neg_while_kernel +{ +/* + * While Kernel with wrong output type for Cond Graph (should be boolean but using int32): + * Main graph: + * Input(1) + * | + * While + * | + * Output(1) + * + * Cond graph: + * CInput(1) + * | + * Less (Const = 10) + * | + * COutput(1) - int32, but should be bool + * + * Body Graph: + * BInput(1) + * | + * Add (Const = 1) + * | + * BOutput(1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x94, 0xfc, 0xff, 0xff, 0x04, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xb8, 0xfc, 0xff, 0xff, 0xbc, 0xfc, 0xff, 0xff, + 0xc0, 0xfc, 0xff, 0xff, 0xc4, 0xfc, 0xff, 0xff, 0xc8, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x10, 0x02, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0xfe, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x48, 0x49, 0x4c, 0x45, 0x5f, 0x42, 0x4f, + 0x44, 0x59, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0xfd, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xf8, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x69, 0x66, 0x6d, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x69, 0x66, 0x6d, + 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x48, 0x49, 0x4c, 0x45, 0x5f, 0x43, 0x4f, + 0x4e, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x24, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc4, 0xfe, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x63, 0x69, 0x66, 0x6d, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x63, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_while_kernel + +class NegTestDataWhileKernel : public NegTestDataBase +{ +public: + NegTestDataWhileKernel() + { + _test_kernel_model_circle = neg_while_kernel::test_kernel_model_circle; + } + + ~NegTestDataWhileKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_WHILE_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/WhileKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/WhileKernel.h new file mode 100644 index 0000000..73c27cd --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/while/WhileKernel.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_TEST_MODELS_WHILE_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_WHILE_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +namespace while_kernel +{ +/* + * While Kernel: + * Main graph: + * Input(1) + * | + * While + * | + * Output(1) + * + * Cond graph: + * CInput(1) + * | + * Less (Const = 10) + * | + * COutput(1) + * + * Body Graph: + * BInput(1) + * | + * Add (Const = 1) + * | + * BOutput(1) + */ +const unsigned char test_kernel_model_circle[] = { + 0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xe2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x94, 0xfc, 0xff, 0xff, 0x04, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xb8, 0xfc, 0xff, 0xff, 0xbc, 0xfc, 0xff, 0xff, + 0xc0, 0xfc, 0xff, 0xff, 0xc4, 0xfc, 0xff, 0xff, 0xc8, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x10, 0x02, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0xfe, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x48, 0x49, 0x4c, 0x45, 0x5f, 0x42, 0x4f, + 0x44, 0x59, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x30, 0xfd, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd0, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xf8, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x69, 0x66, 0x6d, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0x69, 0x66, 0x6d, + 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x48, 0x49, 0x4c, 0x45, 0x5f, 0x43, 0x4f, + 0x4e, 0x44, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x24, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc4, 0xfe, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x63, 0x69, 0x66, 0x6d, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x63, 0x69, 0x66, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x66, 0x6d, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, + 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00}; + +const std::vector input_data = {35}; + +const std::vector reference_output_data = {35}; +} // namespace while_kernel + +template class TestDataWhileKernel : public TestDataBase +{ +public: + TestDataWhileKernel() + { + _input_data = while_kernel::input_data; + _reference_output_data = while_kernel::reference_output_data; + _test_kernel_model_circle = while_kernel::test_kernel_model_circle; + } + + ~TestDataWhileKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &get_input_data_by_index(int i) override final + { + switch (i) + { + case 0: + return _input_data; + default: + assert(false && "Wrong input index"); + } + } + + const std::vector &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_WHILE_KERNEL_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h deleted file mode 100644 index 84147b2..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALArgMax.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H -#define LUCI_INTERPRETER_PAL_ARGMAX_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, - const T2 *axis, const tflite::RuntimeShape &output_shape, - T3 *output_data, const std::greater cmp) -{ - tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h deleted file mode 100644 index 79ed587..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALAveragePool2d.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H -#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H - -#include -#include -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data, - const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) -{ - { - // MARK: At this moment this operation is not supported - assert(false && "AveragePool NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, - int8_t *scratchpad_data) -{ - assert(input_shape.DimensionsCount() == 4); - assert(output_shape.DimensionsCount() == 4); - assert(scratchpad_data != nullptr); - - const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); - assert(batches == 1); - - const int depth = tflite::MatchingDim(input_shape, 3, output_shape, 3); - - cmsis_nn_dims input_dims; - input_dims.n = 1; - input_dims.h = input_shape.Dims(1); - input_dims.w = input_shape.Dims(2); - input_dims.c = depth; - - cmsis_nn_dims output_dims; - output_dims.n = 1; - output_dims.h = output_shape.Dims(1); - output_dims.w = output_shape.Dims(2); - output_dims.c = depth; - - cmsis_nn_pool_params pool_params; - pool_params.stride.h = params.stride_height; - pool_params.stride.w = params.stride_width; - pool_params.padding.h = params.padding_values.height; - pool_params.padding.w = params.padding_values.width; - pool_params.activation.min = params.quantized_activation_min; - pool_params.activation.max = params.quantized_activation_max; - - cmsis_nn_dims filter_dims; - filter_dims.n = 1; - filter_dims.h = params.filter_height; - filter_dims.w = params.filter_width; - filter_dims.c = 1; - - cmsis_nn_context ctx; - ctx.buf = scratchpad_data; - ctx.size = scratchpad_shape.Dims(0); - auto res = arm_avgpool_s8(&ctx, &pool_params, &input_dims, input_data, &filter_dims, &output_dims, - output_data); - assert(res == ARM_MATH_SUCCESS); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &output_shape) - -{ - if (input_data_type == luci_interpreter::DataType::S8) - { - assert(input_shape.DimensionsCount() == 4); - assert(output_shape.DimensionsCount() == 4); - - const int32_t output_width = output_shape.Dims(2); - const int32_t depth = tflite::MatchingDim(input_shape, 3, output_shape, 3); - - const int32_t buf_size = arm_avgpool_s8_get_buffer_size(output_width, depth); - auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); - - luci_interpreter::Shape scratchpad_shape{buf_size * data_type_size}; - scratchpad->resize(scratchpad_shape); - } - else - { - scratchpad->set_allocatable(false); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h deleted file mode 100644 index 9ba3196..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALBatchToSpaceND.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H -#define LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32 *crops_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::BatchToSpaceND( - unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, crops_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h index 7ec64a7..bd47a88 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h +++ b/onert-micro/luci-interpreter/pal/cmsisnn/PALConv2d.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h deleted file mode 100644 index ce5be43..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthToSpace.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H -#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::DepthToSpace(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h deleted file mode 100644 index 5f5e4dd..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALDepthwiseConv2d.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H -#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H - -#include -#include -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void -DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &filter_shape, - const T *filter_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, const tflite::RuntimeShape &output_shape, - T *output_data, const tflite::RuntimeShape &scratchpad_shape, - T *scratchpad_data) -{ - { - // MARK: At this moment this operation is not supported - assert(false && "DepthwiseConvPerChannel NYI"); - (void)params; - (void)output_multiplier; - (void)output_shift; - (void)input_shape; - (void)output_data; - (void)input_data; - (void)filter_shape; - (void)filter_data; - (void)bias_shape; - (void)bias_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void DepthwiseConvPerChannel( - const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) -{ - if (scratchpad_data) - { - cmsis_nn_dw_conv_params dw_conv_params; - dw_conv_params.dilation.h = params.dilation_height_factor; - dw_conv_params.dilation.w = params.dilation_width_factor; - assert(dw_conv_params.dilation.h == 1); - assert(dw_conv_params.dilation.w == 1); - - dw_conv_params.input_offset = params.input_offset; - dw_conv_params.output_offset = params.output_offset; - dw_conv_params.stride.h = params.stride_height; - dw_conv_params.stride.w = params.stride_width; - dw_conv_params.padding.h = params.padding_values.height; - dw_conv_params.padding.w = params.padding_values.width; - - dw_conv_params.activation.min = params.quantized_activation_min; - dw_conv_params.activation.max = params.quantized_activation_max; - dw_conv_params.ch_mult = params.depth_multiplier; - - cmsis_nn_per_channel_quant_params quant_params; - int32_t output_multiplier = params.output_multiplier; - int32_t output_shift = params.output_shift; - - quant_params.multiplier = &output_multiplier; - quant_params.shift = &output_shift; - - assert(dw_conv_params.activation.min <= dw_conv_params.activation.max); - const int batch_size = tflite::MatchingDim(input_shape, 0, output_shape, 0); - const int output_depth = tflite::MatchingDim(filter_shape, 3, output_shape, 3); - if (bias_data) - { - assert(bias_shape.FlatSize() == output_depth); - } - - cmsis_nn_dims input_dims; - input_dims.n = batch_size; - input_dims.h = input_shape.Dims(1); - input_dims.w = input_shape.Dims(2); - input_dims.c = input_shape.Dims(3); - - cmsis_nn_dims filter_dims; - filter_dims.n = filter_shape.Dims(0); - filter_dims.h = filter_shape.Dims(1); - filter_dims.w = filter_shape.Dims(2); - filter_dims.c = output_depth; - - cmsis_nn_dims bias_dims; - bias_dims.n = 1; - bias_dims.h = 1; - bias_dims.w = 1; - bias_dims.c = output_depth; - - cmsis_nn_dims output_dims; - output_dims.n = batch_size; - output_dims.h = output_shape.Dims(1); - output_dims.w = output_shape.Dims(2); - output_dims.c = output_depth; - - cmsis_nn_context ctx; - ctx.buf = scratchpad_data; - ctx.size = scratchpad_shape.Dims(0); - - auto res = arm_depthwise_conv_wrapper_s8(&ctx, &dw_conv_params, &quant_params, &input_dims, - input_data, &filter_dims, filter_data, &bias_dims, - bias_data, &output_dims, output_data); - assert(res == ARM_MATH_SUCCESS); - } - else - { - tflite::reference_integer_ops::DepthwiseConvPerChannel( - params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data); - } -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const tflite::DepthwiseParams ¶ms, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &filter_shape, - const tflite::RuntimeShape &output_shape) -{ - cmsis_nn_dw_conv_params dw_conv_params; - dw_conv_params.dilation.h = params.dilation_height_factor; - dw_conv_params.dilation.w = params.dilation_width_factor; - - if (input_data_type == luci_interpreter::DataType::S8 && dw_conv_params.dilation.h == 1 && - dw_conv_params.dilation.w == 1) - { - const int batch_size = tflite::MatchingDim(input_shape, 0, output_shape, 0); - const int output_depth = tflite::MatchingDim(filter_shape, 3, output_shape, 3); - - cmsis_nn_dims input_dims; - input_dims.n = batch_size; - input_dims.h = input_shape.Dims(1); - input_dims.w = input_shape.Dims(2); - input_dims.c = input_shape.Dims(3); - - cmsis_nn_dims filter_dims; - filter_dims.n = filter_shape.Dims(0); - filter_dims.h = filter_shape.Dims(1); - filter_dims.w = filter_shape.Dims(2); - filter_dims.c = output_depth; - - cmsis_nn_dims output_dims; - output_dims.n = batch_size; - output_dims.h = output_shape.Dims(1); - output_dims.w = output_shape.Dims(2); - output_dims.c = output_depth; - - const int32_t buf_size = arm_depthwise_conv_wrapper_s8_get_buffer_size( - &dw_conv_params, &input_dims, &filter_dims, &output_dims); - - auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); - - luci_interpreter::Shape scratchpad_shape{buf_size * data_type_size}; - scratchpad->resize(scratchpad_shape); - } - else - { - scratchpad->set_allocatable(false); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h deleted file mode 100644 index a11abef..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALDequantize.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H -#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H - -#include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" -#include "PALreference_ops.h" - -namespace luci_interpreter_pal -{ - -template -static inline void Dequantize(tflite::DequantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_integer_ops::Dequantize(params, input_shape, input_data, output_shape, - output_data); -} - -static inline void Dequantize(tflite::DequantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const uint8_t *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h index 87e29ce..32e9057 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h +++ b/onert-micro/luci-interpreter/pal/cmsisnn/PALFullyConnected.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h deleted file mode 100644 index 16bc4ae..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Normalize.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H -#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::L2Normalization(op_params, input_shape, input_data, output_shape, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h index aa3924e..38a302f 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h +++ b/onert-micro/luci-interpreter/pal/cmsisnn/PALL2Pool2D.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h deleted file mode 100644 index 97daddc..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALLeakyRelu.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H -#define LUCI_INTERPRETER_PAL_LEAKYRELU_H - -#include - -namespace luci_interpreter_pal -{ -static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALMul.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALMul.h index e98d8a2..347a97a 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALMul.h +++ b/onert-micro/luci-interpreter/pal/cmsisnn/PALMul.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h deleted file mode 100644 index 5d9ea51..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALQuantize.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H -#define LUCI_INTERPRETER_PAL_QUANTIZE_H - -#include "PALreference_ops.h" - -namespace luci_interpreter_pal -{ -template -static inline void Quantize(tflite::QuantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); -} - -template -static inline void Requantize(const Input *input_data, int32_t size, - int32_t effective_scale_multiplier, int32_t effective_scale_shift, - int32_t input_zero_point, int32_t output_zero_point, - Output *output_data) -{ - tflite::reference_ops::Requantize(input_data, size, effective_scale_multiplier, - effective_scale_shift, input_zero_point, output_zero_point, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h deleted file mode 100644 index 91c28ef..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeBilinear.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H -#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeBilinear(const tflite::ResizeBilinearParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h deleted file mode 100644 index 688eea3..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALResizeNearestNeighbor.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H -#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h deleted file mode 100644 index c1f096c..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALSVDF.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2020 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SVDF_H -#define LUCI_INTERPRETER_PAL_SVDF_H - -#include -#include - -namespace luci_interpreter_pal -{ -static inline void -IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, - const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, int16_t *activation_state_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, - int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, - int scale_2_b, int32_t input_zp, int32_t output_zp) -{ - const int32_t rank = params.rank; - const int32_t batch_size = input_shape.Dims(0); - const int32_t num_filters = weight_feature_shape.Dims(0); - const int32_t memory_size = weight_time_shape.Dims(1); - - cmsis_nn_dims input_dims; - input_dims.n = input_shape.Dims(0); - input_dims.h = input_shape.Dims(1); - - cmsis_nn_dims weights_feature_dims; - weights_feature_dims.n = weight_feature_shape.Dims(0); - weights_feature_dims.h = weight_feature_shape.Dims(1); - - cmsis_nn_dims weights_time_dims; - weights_time_dims.n = weight_time_shape.Dims(0); - weights_time_dims.h = weight_time_shape.Dims(1); - - cmsis_nn_dims bias_dims; - bias_dims.n = bias_shape.Dims(0); - - cmsis_nn_dims state_dims; - state_dims.n = batch_size; - state_dims.h = memory_size * num_filters; - - cmsis_nn_dims output_dims; - output_dims.n = output_shape.Dims(0); - output_dims.h = output_shape.Dims(1); - - cmsis_nn_svdf_params svdf_params; - svdf_params.rank = params.rank; - svdf_params.input_offset = input_zp; - svdf_params.output_offset = output_zp; - - svdf_params.input_activation.min = INT16_MIN; - svdf_params.input_activation.max = INT16_MAX; - - svdf_params.output_activation.min = INT8_MIN; - svdf_params.output_activation.max = INT8_MAX; - - cmsis_nn_per_tensor_quant_params in_quant_params; - in_quant_params.multiplier = scale_1_a; - in_quant_params.shift = scale_1_b; - - cmsis_nn_per_tensor_quant_params out_quant_params; - out_quant_params.multiplier = scale_2_a; - out_quant_params.shift = scale_2_b; - - cmsis_nn_context scratch_ctx; - scratch_ctx.buf = scratchpad_data; - - cmsis_nn_context scratch_output_ctx; - scratch_output_ctx.buf = output_temp_data; - - arm_svdf_s8(&scratch_ctx, &scratch_output_ctx, &svdf_params, &in_quant_params, &out_quant_params, - &input_dims, input_data, &state_dims, activation_state_data, &weights_feature_dims, - weight_feature_data, &weights_time_dims, weight_time_data, &bias_dims, bias_data, - &output_dims, output_data); -} -static inline void -FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const float *input_data, const tflite::RuntimeShape &weight_feature_shape, - const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const float *weight_time_data, const tflite::RuntimeShape &bias_shape, - const float *bias_data, float *scratchpad_data, float *activation_state_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - const int32_t rank = params.rank; - const int32_t batch_size = input_shape.Dims(0); - const int32_t input_size = input_shape.Dims(1); - const int32_t num_filters = weight_feature_shape.Dims(0); - const int32_t num_units = num_filters / rank; - const int32_t memory_size = weight_time_shape.Dims(1); - - // Left shift the activation_state. - { - float *new_state_start = activation_state_data; - const float *old_state_start = activation_state_data + 1; - const float *old_state_end = activation_state_data + batch_size * num_filters * memory_size; - while (old_state_start != old_state_end) - { - *new_state_start++ = *old_state_start++; - } - } - - // Note: no need to clear the latest activation, matmul is not accumulative. - - // Compute conv1d(inputs, weights_feature). - // The activation_state's rightmost column is used to save current cycle - // activation. This is achieved by starting at state_ptr[memory_size - 1] and - // having the stride equal to memory_size. - - // Perform batched matrix vector multiply operation: - { - const float *matrix = weight_feature_data; - const float *vector = input_data; - float *result = &activation_state_data[memory_size - 1]; - float *result_in_batch = result; - for (int i = 0; i < batch_size; ++i) - { - const float *matrix_ptr = matrix; - for (int j = 0; j < num_filters; ++j) - { - float dot_prod = 0.0f; - const float *vector_in_batch = vector + i * input_size; - for (int k = 0; k < input_size; ++k) - { - dot_prod += *matrix_ptr++ * *vector_in_batch++; - } - *result_in_batch = dot_prod; - result_in_batch += memory_size; - } - } - } - - tflite::reference_ops::ApplyTimeWeightsBiasAndActivation( - batch_size, memory_size, num_filters, num_units, rank, weight_time_data, bias_data, - params.activation, activation_state_data, scratchpad_data, output_data); -} - -static inline void SetupScratchpadTensor( - const luci_interpreter::DataType &input_data_type, - const luci_interpreter::DataType &weight_feature_data_type, - luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, - luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, - luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, - const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, - const int32_t batch_size, const int32_t num_filters, const int32_t num_units) -{ - if (input_data_type == luci_interpreter::DataType::FLOAT32 && - (weight_feature_data_type == luci_interpreter::DataType::S8 || - weight_feature_data_type == luci_interpreter::DataType::U8)) - { - (void)input_shape; - (void)weight_time_shape; - (void)scratchpad_3; - (void)scratchpad_4; - (void)scratchpad_5; - (void)scratchpad_6; - - assert(false && "Hybrid type is not supported for cmsisnn"); - } - - // Resize scratchpad_1 tensor - scratchpad_1->resize({batch_size, num_filters}); - - if (input_data_type == luci_interpreter::DataType::S8) - { - // Resize scratchpad_2 for full_integer op - scratchpad_2->resize({batch_size, num_units}); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h deleted file mode 100644 index 1fb1862..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALSoftmax.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H -#define LUCI_INTERPRETER_PAL_SOFTMAX_H - -#include -#include - -namespace luci_interpreter_pal -{ -static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, - float beta) -{ - // Do nothing for mcu - (void)data; - (void)input_scale; - (void)beta; -} - -static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) -{ - int32 input_beta_multiplier; - int input_beta_left_shift; - static const int kScaledDiffIntegerBits = 5; - tflite::PreprocessSoftmaxScaling(beta, input_scale, kScaledDiffIntegerBits, - &input_beta_multiplier, &input_beta_left_shift); - - params->input_multiplier = input_beta_multiplier; - params->input_left_shift = input_beta_left_shift; - params->diff_min = - -tflite::CalculateInputRadius(kScaledDiffIntegerBits, params->input_left_shift); -} - -template -static inline void Softmax(const tflite::SoftmaxParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - // MARK: At this moment this operation doesn't support on mcu - assert(false && "Softmax NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)output_shape; - (void)output_data; -} - -template <> -inline void Softmax(const tflite::SoftmaxParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data) -{ - const int trailing_dim = input_shape.DimensionsCount() - 1; - const int outer_size = tflite::MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); - const int depth = tflite::MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); - const int32_t mult = params.input_multiplier; - const int32_t shift = params.input_left_shift; - const int32_t diff_min = params.diff_min; - - arm_softmax_s8(input_data, outer_size, depth, mult, shift, diff_min, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h deleted file mode 100644 index 7997c71..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToBatchND.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H -#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, - const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32 *paddings_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::SpaceToBatchND( - params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, paddings_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h deleted file mode 100644 index 7866cc2..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALSpaceToDepth.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H -#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALSub.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALSub.h index 2269bc3..ea57578 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALSub.h +++ b/onert-micro/luci-interpreter/pal/cmsisnn/PALSub.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALreference_ops.h b/onert-micro/luci-interpreter/pal/cmsisnn/PALreference_ops.h deleted file mode 100644 index 62c7209..0000000 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALreference_ops.h +++ /dev/null @@ -1,1556 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2017 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_REFERENCE_OPS_H -#define LUCI_INTERPRETER_PAL_REFERENCE_OPS_H - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "third_party/eigen3/Eigen/Core" -#include "fixedpoint/fixedpoint.h" -#include "ruy/profiler/instrumentation.h" // from @ruy -#include "tensorflow/lite/c/common.h" -#include "tensorflow/lite/kernels/internal/common.h" -#include "tensorflow/lite/kernels/internal/quantization_util.h" -#include "tensorflow/lite/kernels/internal/reference/add.h" -#include "tensorflow/lite/kernels/internal/reference/add_n.h" -#include "tensorflow/lite/kernels/internal/reference/arg_min_max.h" -#include "tensorflow/lite/kernels/internal/reference/batch_matmul.h" -#include "tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h" -#include "tensorflow/lite/kernels/internal/reference/binary_function.h" -#include "tensorflow/lite/kernels/internal/reference/cast.h" -#include "tensorflow/lite/kernels/internal/reference/ceil.h" -#include "tensorflow/lite/kernels/internal/reference/comparisons.h" -#include "tensorflow/lite/kernels/internal/reference/concatenation.h" -#include "tensorflow/lite/kernels/internal/reference/conv.h" -#include "tensorflow/lite/kernels/internal/reference/depth_to_space.h" -#include "tensorflow/lite/kernels/internal/reference/dequantize.h" -#include "tensorflow/lite/kernels/internal/reference/div.h" -#include "tensorflow/lite/kernels/internal/reference/elu.h" -#include "tensorflow/lite/kernels/internal/reference/exp.h" -#include "tensorflow/lite/kernels/internal/reference/fill.h" -#include "tensorflow/lite/kernels/internal/reference/floor.h" -#include "tensorflow/lite/kernels/internal/reference/floor_div.h" -#include "tensorflow/lite/kernels/internal/reference/floor_mod.h" -#include "tensorflow/lite/kernels/internal/reference/fully_connected.h" -#include "tensorflow/lite/kernels/internal/reference/gather.h" -#include "tensorflow/lite/kernels/internal/reference/hard_swish.h" -#include "tensorflow/lite/kernels/internal/reference/l2normalization.h" -#include "tensorflow/lite/kernels/internal/reference/leaky_relu.h" -#include "tensorflow/lite/kernels/internal/reference/log_softmax.h" -#include "tensorflow/lite/kernels/internal/reference/logistic.h" -#include "tensorflow/lite/kernels/internal/reference/maximum_minimum.h" -#include "tensorflow/lite/kernels/internal/reference/mul.h" -#include "tensorflow/lite/kernels/internal/reference/neg.h" -#include "tensorflow/lite/kernels/internal/reference/pad.h" -#include "tensorflow/lite/kernels/internal/reference/pooling.h" -#include "tensorflow/lite/kernels/internal/reference/prelu.h" -#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" -#include "tensorflow/lite/kernels/internal/reference/quantize.h" -#include "tensorflow/lite/kernels/internal/reference/reduce.h" -#include "tensorflow/lite/kernels/internal/reference/requantize.h" -#include "tensorflow/lite/kernels/internal/reference/resize_bilinear.h" -#include "tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h" -#include "tensorflow/lite/kernels/internal/reference/round.h" -#include "tensorflow/lite/kernels/internal/reference/softmax.h" -#include "tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h" -#include "tensorflow/lite/kernels/internal/reference/space_to_depth.h" -#include "tensorflow/lite/kernels/internal/reference/strided_slice.h" -#include "tensorflow/lite/kernels/internal/reference/string_comparisons.h" -#include "tensorflow/lite/kernels/internal/reference/sub.h" -#include "tensorflow/lite/kernels/internal/reference/tanh.h" -#include "tensorflow/lite/kernels/internal/reference/transpose.h" -#include "tensorflow/lite/kernels/internal/reference/transpose_conv.h" -#include "tensorflow/lite/kernels/internal/strided_slice_logic.h" -#include "tensorflow/lite/kernels/internal/tensor.h" -#include "tensorflow/lite/kernels/internal/types.h" -namespace tflite -{ - -namespace reference_ops -{ - -template -inline void Relu(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T lower = 0; - const T clamped = val < lower ? lower : val; - output_data[i] = clamped; - } -} - -template -inline void Relu1(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Relu1 (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T upper = 1; - const T lower = -1; - const T clamped = val > upper ? upper : val < lower ? lower : val; - output_data[i] = clamped; - } -} - -inline void Relu6(const RuntimeShape &input_shape, const float *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - ruy::profiler::ScopeLabel label("Relu6 (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const float val = input_data[i]; - const float upper = 6; - const float lower = 0; - const float clamped = val > upper ? upper : val < lower ? lower : val; - output_data[i] = clamped; - } -} - -template -inline void ReluX(const tflite::ReluParams ¶ms, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const int32 val = static_cast(input_data[i]); - int32 clamped = params.output_offset + MultiplyByQuantizedMultiplier(val - params.input_offset, - params.output_multiplier, - params.output_shift); - clamped = std::max(params.quantized_activation_min, clamped); - clamped = std::min(params.quantized_activation_max, clamped); - output_data[i] = static_cast(clamped); - } -} - -template -inline void ReluX(const tflite::ActivationParams ¶ms, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - const T max_value = params.quantized_activation_max; - const T min_value = params.quantized_activation_min; - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T clamped = val > max_value ? max_value : val < min_value ? min_value : val; - output_data[i] = clamped; - } -} - -// TODO(jiawen): We can implement BroadcastMul on buffers of arbitrary -// dimensionality if the runtime code does a single loop over one dimension -// that handles broadcasting as the base case. The code generator would then -// generate max(D1, D2) nested for loops. -inline void BroadcastMulFivefold(const ArithmeticParams &unswitched_params, - const RuntimeShape &unswitched_input1_shape, - const uint8 *unswitched_input1_data, - const RuntimeShape &unswitched_input2_shape, - const uint8 *unswitched_input2_data, - const RuntimeShape &output_shape, uint8 *output_data) -{ - ArithmeticParams switched_params = unswitched_params; - switched_params.input1_offset = unswitched_params.input2_offset; - switched_params.input2_offset = unswitched_params.input1_offset; - - const bool use_unswitched = unswitched_params.broadcast_category == - tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast; - - const ArithmeticParams ¶ms = use_unswitched ? unswitched_params : switched_params; - const uint8 *input1_data = use_unswitched ? unswitched_input1_data : unswitched_input2_data; - const uint8 *input2_data = use_unswitched ? unswitched_input2_data : unswitched_input1_data; - - // Fivefold nested loops. The second input resets its position for each - // iteration of the second loop. The first input resets its position at the - // beginning of the fourth loop. The innermost loop is an elementwise Mul of - // sections of the arrays. - uint8 *output_data_ptr = output_data; - const uint8 *input1_data_ptr = input1_data; - const uint8 *input2_data_reset = input2_data; - int y0 = params.broadcast_shape[0]; - int y1 = params.broadcast_shape[1]; - int y2 = params.broadcast_shape[2]; - int y3 = params.broadcast_shape[3]; - int y4 = params.broadcast_shape[4]; - for (int i0 = 0; i0 < y0; ++i0) - { - const uint8 *input2_data_ptr; - for (int i1 = 0; i1 < y1; ++i1) - { - input2_data_ptr = input2_data_reset; - for (int i2 = 0; i2 < y2; ++i2) - { - for (int i3 = 0; i3 < y3; ++i3) - { - MulElementwise(y4, params, input1_data_ptr, input2_data_ptr, output_data_ptr); - input2_data_ptr += y4; - output_data_ptr += y4; - } - input1_data_ptr += y4; - } - } - input2_data_reset = input2_data_ptr; - } -} - -inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16 *input1_data, const RuntimeShape &input2_shape, - const int16 *input2_data, const RuntimeShape &output_shape, int16 *output_data) -{ - ruy::profiler::ScopeLabel label("Mul/Int16"); - - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - - for (int i = 0; i < flat_size; i++) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - - F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); - output_data[i] = unclamped_result.raw(); - } -} - -inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16 *input1_data, const RuntimeShape &input2_shape, - const int16 *input2_data, const RuntimeShape &output_shape, uint8 *output_data) -{ - ruy::profiler::ScopeLabel label("Mul/Int16Uint8"); - int32 output_offset = params.output_offset; - int32 output_activation_min = params.quantized_activation_min; - int32 output_activation_max = params.quantized_activation_max; - TFLITE_DCHECK_LE(output_activation_min, output_activation_max); - - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - - for (int i = 0; i < flat_size; i++) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - - F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); - int16 rescaled_result = gemmlowp::RoundingDivideByPOT(unclamped_result.raw(), 8); - int16 clamped_result = std::min(output_activation_max - output_offset, rescaled_result); - clamped_result = std::max(output_activation_min - output_offset, clamped_result); - output_data[i] = output_offset + clamped_result; - } -} - -inline void Sub16(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16_t *input1_data, const RuntimeShape &input2_shape, - const int16_t *input2_data, const RuntimeShape &output_shape, - int16_t *output_data) -{ - ruy::profiler::ScopeLabel label("Sub/Int16"); - const int input1_shift = params.input1_shift; - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - const int16 output_activation_min = params.quantized_activation_min; - const int16 output_activation_max = params.quantized_activation_max; - - TFLITE_DCHECK(input1_shift == 0 || params.input2_shift == 0); - TFLITE_DCHECK_LE(input1_shift, 0); - TFLITE_DCHECK_LE(params.input2_shift, 0); - const int16 *not_shift_input = input1_shift == 0 ? input1_data : input2_data; - const int16 *shift_input = input1_shift == 0 ? input2_data : input1_data; - const int input_right_shift = input1_shift == 0 ? -params.input2_shift : -input1_shift; - - if (input1_shift == 0) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - for (int i = 0; i < flat_size; ++i) - { - F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); - F0 scaled_input = - F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); - F0 result = SaturatingSub(input_ready_scaled, scaled_input); - const int16 raw_output = result.raw(); - const int16 clamped_output = - std::min(output_activation_max, std::max(output_activation_min, raw_output)); - output_data[i] = clamped_output; - } - } - else - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - for (int i = 0; i < flat_size; ++i) - { - F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); - F0 scaled_input = - F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); - F0 result = SaturatingSub(scaled_input, input_ready_scaled); - const int16 raw_output = result.raw(); - const int16 clamped_output = - std::min(output_activation_max, std::max(output_activation_min, raw_output)); - output_data[i] = clamped_output; - } - } -} - -template -void Pack(const PackParams ¶ms, const RuntimeShape *const *input_shapes, - const Scalar *const *input_data, const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("Pack"); - const int dimensions = output_shape.DimensionsCount(); - int axis = params.axis; - int inputs_count = params.inputs_count; - - int outer_size = 1; - for (int i = 0; i < axis; i++) - { - outer_size *= output_shape.Dims(i); - } - int copy_size = 1; - for (int i = params.axis + 1; i < dimensions; i++) - { - copy_size *= output_shape.Dims(i); - } - TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); - - for (int i = 0; i < inputs_count; ++i) - { - for (int k = 0; k < outer_size; k++) - { - const Scalar *input_ptr = input_data[i] + copy_size * k; - int loc = k * inputs_count * copy_size + i * copy_size; - memcpy(output_data + loc, input_ptr, copy_size * sizeof(Scalar)); - } - } -} - -template -void Unpack(const UnpackParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *const *output_datas) -{ - ruy::profiler::ScopeLabel label("Unpack"); - const int dimensions = input_shape.DimensionsCount(); - const int outputs_count = params.num_split; - - int outer_size = 1; - int axis = params.axis; - if (axis < 0) - { - axis += dimensions; - } - TFLITE_DCHECK_GE(axis, 0); - TFLITE_DCHECK_LT(axis, dimensions); - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - int copy_size = 1; - for (int i = axis + 1; i < dimensions; ++i) - { - copy_size *= input_shape.Dims(i); - } - TFLITE_DCHECK_EQ(output_shape.FlatSize(), copy_size * outer_size); - - for (int i = 0; i < outputs_count; ++i) - { - for (int k = 0; k < outer_size; k++) - { - Scalar *output_ptr = output_datas[i] + copy_size * k; - int loc = k * outputs_count * copy_size + i * copy_size; - memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); - } - } -} - -template -void PackWithScaling(const PackParams ¶ms, const RuntimeShape *const *input_shapes, - const uint8 *const *input_data, const RuntimeShape &output_shape, - uint8 *output_data) -{ - ruy::profiler::ScopeLabel label("PackWithScaling"); - const int dimensions = output_shape.DimensionsCount(); - int axis = params.axis; - const int32 *input_zeropoint = params.input_zeropoint; - const float *input_scale = params.input_scale; - int inputs_count = params.inputs_count; - const int32 output_zeropoint = params.output_zeropoint; - const float output_scale = params.output_scale; - - int outer_size = 1; - for (int i = 0; i < axis; i++) - { - outer_size *= output_shape.Dims(i); - } - int copy_size = 1; - for (int i = axis + 1; i < dimensions; i++) - { - copy_size *= output_shape.Dims(i); - } - TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); - - Scalar *output_ptr = output_data; - const float inverse_output_scale = 1.f / output_scale; - for (int k = 0; k < outer_size; k++) - { - for (int i = 0; i < inputs_count; ++i) - { - if (input_zeropoint[i] == output_zeropoint && input_scale[i] == output_scale) - { - memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); - } - else - { - assert(false); - const float scale = input_scale[i] * inverse_output_scale; - const float bias = -input_zeropoint[i] * scale; - auto input_ptr = input_data[i]; - for (int j = 0; j < copy_size; ++j) - { - const int value = - static_cast(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; - output_ptr[j] = static_cast(std::max(std::min(255, value), 0)); - } - } - output_ptr += copy_size; - } - } -} - -template -void DepthConcatenation(const ConcatenationParams ¶ms, const RuntimeShape *const *input_shapes, - const Scalar *const *input_data, const RuntimeShape &output_shape, - Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("DepthConcatenation"); - auto params_copy = params; - params_copy.axis = 3; - Concatenation(params_copy, input_shapes, input_data, output_shape, output_data); -} - -inline void LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, - const float *input_data, const RuntimeShape &unextended_prev_activ_shape, - const float *prev_activ_data, const RuntimeShape &weights_shape, - const float *weights_data, const RuntimeShape &unextended_bias_shape, - const float *bias_data, const RuntimeShape &unextended_prev_state_shape, - const float *prev_state_data, - const RuntimeShape &unextended_output_state_shape, float *output_state_data, - const RuntimeShape &unextended_output_activ_shape, float *output_activ_data, - const RuntimeShape &unextended_concat_temp_shape, float *concat_temp_data, - const RuntimeShape &unextended_activ_temp_shape, float *activ_temp_data) -{ - TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); - const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); - const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); - const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); - const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); - const RuntimeShape output_state_shape = - RuntimeShape::ExtendedShape(4, unextended_output_state_shape); - const RuntimeShape output_activ_shape = - RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); - const RuntimeShape concat_temp_shape = - RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); - const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); - TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); - - const int weights_dim_count = weights_shape.DimensionsCount(); - const int batches = MatchingDim(input_shape, 0, prev_activ_shape, 0, prev_state_shape, 0, - output_state_shape, 0, output_activ_shape, 0); - const int height = MatchingDim(input_shape, 1, prev_activ_shape, 1, prev_state_shape, 1, - output_state_shape, 1, output_activ_shape, 1); - const int width = MatchingDim(input_shape, 2, prev_activ_shape, 2, prev_state_shape, 2, - output_state_shape, 2, output_activ_shape, 2); - const int input_depth = input_shape.Dims(3); - const int prev_activ_depth = prev_activ_shape.Dims(3); - const int total_input_depth = prev_activ_depth + input_depth; - TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); - TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); - const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); - TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); - TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); - const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, - 3, output_activ_shape, 3); - TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); - - // Concatenate prev_activ and input data together - std::vector concat_input_arrays_data; - std::vector concat_input_arrays_shapes; - concat_input_arrays_data.push_back(input_data); - concat_input_arrays_data.push_back(prev_activ_data); - concat_input_arrays_shapes.push_back(&input_shape); - concat_input_arrays_shapes.push_back(&prev_activ_shape); - tflite::ConcatenationParams concat_params; - concat_params.axis = 3; - concat_params.inputs_count = concat_input_arrays_data.size(); - Concatenation(concat_params, &(concat_input_arrays_shapes[0]), &(concat_input_arrays_data[0]), - concat_temp_shape, concat_temp_data); - - // Fully connected - tflite::FullyConnectedParams fc_params; - fc_params.float_activation_min = std::numeric_limits::lowest(); - fc_params.float_activation_max = std::numeric_limits::max(); - FullyConnected(fc_params, concat_temp_shape, concat_temp_data, weights_shape, weights_data, - bias_shape, bias_data, activ_temp_shape, activ_temp_data); - - // Memory state update (the LSTM "guts") - for (int b = 0; b < batches; ++b) - { - for (int w = 0; w < width; ++w) - { - for (int h = 0; h < height; ++h) - { - for (int c = 0; c < output_depth; ++c) - { - const float input_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 0 * output_depth + c)])); - const float new_input = - std::tanh(activ_temp_data[Offset(activ_temp_shape, b, h, w, 1 * output_depth + c)]); - const float forget_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 2 * output_depth + c)])); - const float output_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 3 * output_depth + c)])); - const float new_state = - input_gate * new_input + - forget_gate * prev_state_data[Offset(prev_state_shape, b, h, w, c)]; - output_state_data[Offset(output_state_shape, b, h, w, c)] = new_state; - output_activ_data[Offset(output_activ_shape, b, h, w, c)] = - output_gate * std::tanh(new_state); - } - } - } - } -} - -// Quantized LSTM cell implementation. -// The quantization of the input, output arrays is as follows: -// - The input activations are quantized as uint8 on the interval -// [-1, 127/128]. -// The rationale for that is that is the natural interval for output -// activations (see next point) and these need to be concatenated together. -// We could accommodate different ranges by re-scaling, but we empirically -// found that setting the input activations range to be [-1, 127/128] in the -// first place, removing the need for re-scaling, greatly improves accuracy. -// - The output activations are quantized as uint8 on the interval -// [-1, 127/128]. -// The rationale for that is that the definition of a LSTM cell makes them -// intrinsically constrained in [-1, 1]; tweaking that to [-1, 127/128] -// makes for simpler, more accurate fixed-point arithmetic. -// - The output-at-previous-timestep state array is obviously quantized as -// the output activations. -// - The internal LSTM memory (not the output-at-previous-timestep, the other -// internal state array) is int16-quantized and may use any power-of-two, -// symmetric range i.e. [-2^N, 2^N * 32767/32768] for any N, which we call -// StateIntegerBits below, see the below discussion of that template -// parameter ("The StateIntegerBits template parameter"). -// - The output of the internal fully-connected node is int16-quantized -// on the interval [-8, 8 * 32767/32768], the rationale for which is -// explained just below ("Why [-8, 8] for fully-connected output?"). -// -// -// === The StateIntegerBits template parameter === -// -// The StateIntegerBits template parameter controls the fixed-point format used -// to represent the internal memory of the LSTM cell (not the -// output-at-previous-timestep, the other internal state array). It's currently -// a template parameter so that the model can control that. The most typical -// value for StateIntegerBits is 4. Other plausible values are anywhere between -// 3 and 5. We might eventually standardize on a single supported value, e.g. 4, -// and drop that template parameter. The reason why it can't be a runtime -// parameter is that this controls the fixed-point format used, i.e. we need to -// generate actually different code based on it. In particular, we generate code -// for a fixed-point tanh() implementation for that format, which internally -// uses a fixed-point exp() implementation, which internally uses a -// barrel-shifter with a number of steps that depends on StateIntegerBits. -// Another consequence of that is that a higher value of StateIntegerBits -// results in a more expensive implementation (more barrel shifter steps -// needed). -// -// -// === Why [-8, 8] for fully-connected output? === -// -// This array is only fed to Logistic and Tanh functions, for which -// the quantized implementation will want to use fixed-point arithmetic, -// requiring a power-of-two representation interval. Thus, we should right -// away quantize this array to a power-of-two interval; otherwise, -// implementation will need to rescale that, losing any benefit that a tighter -// representation interval might otherwise yield, while introducing some -// numerical error and computational overhead. -// -// Now, Logistic and Tanh -// are nearly constant (nearly equal to their horizontal asymptotes) -// outside of a small bounded interval around 0: -// -// Logistic(4) = 1 - 1.8e-2 Tanh(4) = 1 - 6.7e-4 -// Logistic(8) = 1 - 3.4e-4 Tanh(8) = 1 - 2.3e-7 -// Logistic(16) = 1 - 1.1e-7 Tanh(16) = 1 - 2.5e-14 -// -// From this, we see that clamping to [-4, 4] would be too inaccurate -// (the error of 1.8e-2 on Logistic would be felt even in 8bit precision) -// while clamping to [-16, 16] would make no difference even in float32. -// However, for a fixed-point implementation in 16-bit integers, using 5 -// integer bits to represent the [-16, 16] range would leave only 11 -// fractional bits, giving an increment of 2^-11 = 4.9e-4 between consecutive -// representable values. Notice that is higher than the -// worst-case clamping error with clamping to [-8, 8]: 3.4e-4 for Logistic. -// Using [-8, 8] thus seems like the better compromise overall, enjoying -// an increment of 2.4e-4 between representable values and a worst-case -// clamping error of 3.4e-4, both better than the increment of 4.9e-4 with -// [-16, 16]. -// -// Moreover, all other things being equal, it is nice to choose the narrower -// representation range, as that makes the implementation of fixed-point -// math functions a little cheaper (each integer bit requires an additional -// barrel-shifter atep in the implementation of exp(-x)). That is further -// reason to prefer [-8, 8] over [-16, 16]. The choice of [-16, 16] would make -// sense for 32-bit float or 32-bit fixed-point quantization, but we are -// aiming for 16-bit fixed-point quantization of these internal nodes here. -// -template -inline void -LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, - const uint8 *input_data_uint8, const RuntimeShape &unextended_prev_activ_shape, - const uint8 *prev_activ_data_uint8, const RuntimeShape &weights_shape, - const uint8 *weights_data_uint8, const RuntimeShape &unextended_bias_shape, - const int32 *bias_data_int32, const RuntimeShape &unextended_prev_state_shape, - const int16 *prev_state_data_int16, const RuntimeShape &unextended_output_state_shape, - int16 *output_state_data_int16, const RuntimeShape &unextended_output_activ_shape, - uint8 *output_activ_data_uint8, const RuntimeShape &unextended_concat_temp_shape, - uint8 *concat_temp_data_uint8, const RuntimeShape &unextended_activ_temp_shape, - int16 *activ_temp_data_int16, void *gemmlowp_context) -{ - (void)gemmlowp_context; // only used in optimized code. - int32 weights_zero_point = params.weights_zero_point; - int32 accum_multiplier = params.accum_multiplier; - int accum_shift = params.accum_shift; - TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); - const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); - const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); - const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); - const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); - const RuntimeShape output_state_shape = - RuntimeShape::ExtendedShape(4, unextended_output_state_shape); - const RuntimeShape output_activ_shape = - RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); - const RuntimeShape concat_temp_shape = - RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); - const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); - TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); - - // Gather dimensions information, and perform consistency checks. - const int weights_dim_count = weights_shape.DimensionsCount(); - const int outer_size = MatchingFlatSizeSkipDim(input_shape, 3, prev_activ_shape, prev_state_shape, - output_state_shape, output_activ_shape); - const int input_depth = input_shape.Dims(3); - const int prev_activ_depth = prev_activ_shape.Dims(3); - const int total_input_depth = prev_activ_depth + input_depth; - TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); - const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); - TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); - TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); - TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); - const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, - 3, output_activ_shape, 3); - TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); - const int fc_batches = FlatSizeSkipDim(activ_temp_shape, 3); - const int fc_output_depth = - MatchingDim(weights_shape, weights_dim_count - 2, activ_temp_shape, 3); - const int fc_accum_depth = total_input_depth; - TFLITE_DCHECK_EQ(fc_output_depth, 4 * output_depth); - - // Depth-concatenate prev_activ and input data together. - uint8 const *concat_input_arrays_data[2] = {input_data_uint8, prev_activ_data_uint8}; - const RuntimeShape *concat_input_arrays_shapes[2] = {&input_shape, &prev_activ_shape}; - tflite::ConcatenationParams concat_params; - concat_params.axis = 3; - concat_params.inputs_count = 2; - Concatenation(concat_params, concat_input_arrays_shapes, concat_input_arrays_data, - concat_temp_shape, concat_temp_data_uint8); - - // Implementation of the fully connected node inside the LSTM cell. - // The operands are 8-bit integers, the accumulators are internally 32bit - // integers, and the output is 16-bit fixed-point with 3 integer bits so - // the output range is [-2^3, 2^3] == [-8, 8]. The rationale for that - // is explained in the function comment above. - for (int b = 0; b < fc_batches; ++b) - { - for (int out_c = 0; out_c < fc_output_depth; ++out_c) - { - // Internal accumulation. - // Initialize accumulator with the bias-value. - int32 accum = bias_data_int32[out_c]; - // Accumulation loop. - for (int d = 0; d < fc_accum_depth; ++d) - { - int16 input_val = concat_temp_data_uint8[b * fc_accum_depth + d] - 128; - int16 weights_val = weights_data_uint8[out_c * fc_accum_depth + d] - weights_zero_point; - accum += input_val * weights_val; - } - // Down-scale the final int32 accumulator to the scale used by our - // (16-bit, using 3 integer bits) fixed-point format. The quantized - // multiplier and shift here have been pre-computed offline - // (e.g. by toco). - accum = MultiplyByQuantizedMultiplier(accum, accum_multiplier, accum_shift); - // Saturate, cast to int16, and store to the temporary activations array. - accum = std::max(-32768, std::min(32767, static_cast(accum))); - activ_temp_data_int16[out_c + fc_output_depth * b] = accum; - } - } - - // Rest of the LSTM cell: tanh and logistic math functions, and some adds - // and muls, all done in 16-bit fixed-point. - for (int b = 0; b < outer_size; ++b) - { - for (int c = 0; c < output_depth; ++c) - { - // Define the fixed-point data types that we will use here. All use - // int16 as the underlying integer type i.e. all are 16-bit fixed-point. - // They only differ by the number of integral vs. fractional bits, - // determining the range of values that they can represent. - // - // F0 uses 0 integer bits, range [-1, 1]. - // This is the return type of math functions such as tanh, logistic, - // whose range is in [-1, 1]. - using F0 = gemmlowp::FixedPoint; - // F3 uses 3 integer bits, range [-8, 8]. - // This is the range of the previous fully-connected node's output, - // which is our input here. - using F3 = gemmlowp::FixedPoint; - // FS uses StateIntegerBits integer bits, range [-2^StateIntegerBits, - // 2^StateIntegerBits]. It's used to represent the internal state, whose - // number of integer bits is currently dictated by the model. See comment - // on the StateIntegerBits template parameter above. - using FS = gemmlowp::FixedPoint; - // Implementation of input gate, using fixed-point logistic function. - F3 input_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 0 * output_depth + c]); - F0 input_gate_output = gemmlowp::logistic(input_gate_input); - // Implementation of input modulation gate, using fixed-point tanh - // function. - F3 input_modulation_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 1 * output_depth + c]); - F0 input_modulation_gate_output = gemmlowp::tanh(input_modulation_gate_input); - // Implementation of forget gate, using fixed-point logistic function. - F3 forget_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 2 * output_depth + c]); - F0 forget_gate_output = gemmlowp::logistic(forget_gate_input); - // Implementation of output gate, using fixed-point logistic function. - F3 output_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 3 * output_depth + c]); - F0 output_gate_output = gemmlowp::logistic(output_gate_input); - // Implementation of internal multiplication nodes, still in fixed-point. - F0 input_times_input_modulation = input_gate_output * input_modulation_gate_output; - FS prev_state = FS::FromRaw(prev_state_data_int16[b * output_depth + c]); - FS prev_state_times_forget_state = forget_gate_output * prev_state; - // Implementation of internal addition node, saturating. - FS new_state = - gemmlowp::SaturatingAdd(gemmlowp::Rescale(input_times_input_modulation), - prev_state_times_forget_state); - // Implementation of last internal Tanh node, still in fixed-point. - // Since a Tanh fixed-point implementation is specialized for a given - // number or integer bits, and each specialization can have a substantial - // code size, and we already used above a Tanh on an input with 3 integer - // bits, and per the table in the above function comment there is no - // significant accuracy to be lost by clamping to [-8, +8] for a - // 3-integer-bits representation, let us just do that. This helps people - // porting this to targets where code footprint must be minimized. - F3 new_state_f3 = gemmlowp::Rescale<3>(new_state); - F0 output_activ_int16 = output_gate_output * gemmlowp::tanh(new_state_f3); - // Store the new internal state back to memory, as 16-bit integers. - // Note: here we store the original value with StateIntegerBits, not - // the rescaled 3-integer-bits value fed to tanh. - output_state_data_int16[b * output_depth + c] = new_state.raw(); - // Down-scale the output activations to 8-bit integers, saturating, - // and store back to memory. - int16 rescaled_output_activ = gemmlowp::RoundingDivideByPOT(output_activ_int16.raw(), 8); - int16 clamped_output_activ = - std::max(-128, std::min(127, rescaled_output_activ)); - output_activ_data_uint8[b * output_depth + c] = 128 + clamped_output_activ; - } - } -} - -template -void Split(const SplitParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape *const *output_shapes, Scalar *const *output_data) -{ - ruy::profiler::ScopeLabel label("Split"); - const int split_dimensions = input_shape.DimensionsCount(); - int axis = params.axis < 0 ? params.axis + split_dimensions : params.axis; - int outputs_count = params.num_split; - TFLITE_DCHECK_LT(axis, split_dimensions); - - int64_t split_size = 0; - for (int i = 0; i < outputs_count; i++) - { - TFLITE_DCHECK_EQ(output_shapes[i]->DimensionsCount(), split_dimensions); - for (int j = 0; j < split_dimensions; j++) - { - if (j != axis) - { - MatchingDim(*output_shapes[i], j, input_shape, j); - } - } - split_size += output_shapes[i]->Dims(axis); - } - TFLITE_DCHECK_EQ(split_size, input_shape.Dims(axis)); - int64_t outer_size = 1; - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - // For all output arrays, - // FlatSize() = outer_size * Dims(axis) * base_inner_size; - int64_t base_inner_size = 1; - for (int i = axis + 1; i < split_dimensions; ++i) - { - base_inner_size *= input_shape.Dims(i); - } - - const Scalar *input_ptr = input_data; - for (int k = 0; k < outer_size; k++) - { - for (int i = 0; i < outputs_count; ++i) - { - const int copy_size = output_shapes[i]->Dims(axis) * base_inner_size; - memcpy(output_data[i] + k * copy_size, input_ptr, copy_size * sizeof(Scalar)); - input_ptr += copy_size; - } - } -} - -inline int NodeOffset(int b, int h, int w, int height, int width) -{ - return (b * height + h) * width + w; -} - -inline void LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, - const RuntimeShape &input_shape, const float *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - const int trailing_dim = input_shape.DimensionsCount() - 1; - const int outer_size = MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); - const int depth = MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); - - for (int i = 0; i < outer_size; ++i) - { - for (int c = 0; c < depth; ++c) - { - const int begin_input_c = std::max(0, static_cast(c - op_params.range)); - const int end_input_c = std::min(depth, static_cast(c + op_params.range)); - float accum = 0.f; - for (int input_c = begin_input_c; input_c < end_input_c; ++input_c) - { - const float input_val = input_data[i * depth + input_c]; - accum += input_val * input_val; - } - const float multiplier = std::pow(op_params.bias + op_params.alpha * accum, -op_params.beta); - output_data[i * depth + c] = input_data[i * depth + c] * multiplier; - } - } -} - -inline void Dequantize(const RuntimeShape &input_shape, const Eigen::half *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; i++) - { - output_data[i] = static_cast(input_data[i]); - } -} - -inline void FakeQuant(const tflite::FakeQuantParams &op_params, const RuntimeShape &input_shape, - const float *input_data, const RuntimeShape &output_shape, float *output_data) -{ - ruy::profiler::ScopeLabel label("FakeQuant"); - float rmin = op_params.minmax.min; - float rmax = op_params.minmax.max; - int num_bits = op_params.num_bits; - // 0 should always be a representable value. Let's assume that the initial - // min,max range contains 0. - TFLITE_DCHECK_LE(rmin, 0.0f); - TFLITE_DCHECK_GE(rmax, 0.0f); - TFLITE_DCHECK_LT(rmin, rmax); - - // Code matches tensorflow's FakeQuantWithMinMaxArgsFunctor. - int quant_min = 0; - int quant_max = (1 << num_bits) - 1; - float nudged_min, nudged_max, nudged_scale; - NudgeQuantizationRange(rmin, rmax, quant_min, quant_max, &nudged_min, &nudged_max, &nudged_scale); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - FakeQuantizeArray(nudged_scale, nudged_min, nudged_max, input_data, output_data, flat_size); -} - -// Common subroutine for both `GatherNd` and `GatherNdString`. -struct GatherNdHelperResult -{ - int n_slices; - int slice_size; - int indices_nd; - std::vector dims_to_count; -}; - -// Returns common values being used on both `GatherNd` and `GatherNdString`. -inline GatherNdHelperResult GatherNdHelper(const RuntimeShape ¶ms_shape, - const RuntimeShape &indices_shape) -{ - GatherNdHelperResult ret; - ret.n_slices = 1; - ret.slice_size = 1; - const int indices_dims = indices_shape.DimensionsCount(); - ret.indices_nd = indices_shape.Dims(indices_dims - 1); - const int params_dims = params_shape.DimensionsCount(); - for (int i = 0; i < indices_dims - 1; ++i) - { - ret.n_slices *= indices_shape.Dims(i); - } - for (int i = ret.indices_nd; i < params_dims; ++i) - { - ret.slice_size *= params_shape.Dims(i); - } - - int remain_flat_size = params_shape.FlatSize(); - ret.dims_to_count = std::vector(ret.indices_nd, 0); - for (int i = 0; i < ret.indices_nd; ++i) - { - ret.dims_to_count[i] = remain_flat_size / params_shape.Dims(i); - remain_flat_size = ret.dims_to_count[i]; - } - - return ret; -} - -template -inline void GatherNd(const RuntimeShape ¶ms_shape, const ParamsT *params_data, - const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &output_shape, ParamsT *output_data) -{ - ruy::profiler::ScopeLabel label("GatherNd"); - - const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); - for (int i = 0; i < res.n_slices; ++i) - { - int from_pos = 0; - for (int j = 0; j < res.indices_nd; ++j) - { - from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; - } - std::memcpy(output_data + i * res.slice_size, params_data + from_pos, - sizeof(ParamsT) * res.slice_size); - } -} - -#ifndef TF_LITE_STATIC_MEMORY -template -inline void GatherNdString(const RuntimeShape ¶ms_shape, const TfLiteTensor *params_data, - const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &output_shape, TfLiteTensor *output_data) -{ - ruy::profiler::ScopeLabel label("GatherNdString"); - - const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); - DynamicBuffer buffer; - for (int i = 0; i < res.n_slices; ++i) - { - int from_pos = 0; - for (int j = 0; j < res.indices_nd; ++j) - { - from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; - } - for (int j = 0; j < res.slice_size; ++j) - { - buffer.AddString(GetString(params_data, from_pos + j)); - } - } - buffer.WriteToTensor(output_data, /*new_shape=*/nullptr); -} -#endif - -template -inline void ScatterNd(const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &updates_shape, const UpdatesT *updates_data, - const RuntimeShape &output_shape, UpdatesT *output_data) -{ - ruy::profiler::ScopeLabel label("ScatterNd"); - - int n_slices = 1; - int slice_size = 1; - const int outer_dims = indices_shape.DimensionsCount() - 1; - const int indices_nd = indices_shape.Dims(outer_dims); - const int updates_dims = updates_shape.DimensionsCount(); - for (int i = 0; i < outer_dims; ++i) - { - n_slices *= indices_shape.Dims(i); - } - for (int i = outer_dims; i < updates_dims; ++i) - { - slice_size *= updates_shape.Dims(i); - } - - int output_flat_size = output_shape.FlatSize(); - int remain_flat_size = output_flat_size; - std::vector dims_to_count(indices_nd, 0); - for (int i = 0; i < indices_nd; ++i) - { - dims_to_count[i] = remain_flat_size / output_shape.Dims(i); - remain_flat_size = dims_to_count[i]; - } - - memset(output_data, 0, sizeof(UpdatesT) * output_flat_size); - for (int i = 0; i < n_slices; ++i) - { - int to_pos = 0; - for (int j = 0; j < indices_nd; ++j) - { - IndicesT idx = indices_data[i * indices_nd + j]; - TFLITE_DCHECK(0 <= idx && idx < output_shape.Dims(j)); - to_pos += idx * dims_to_count[j]; - } - for (int j = 0; j < slice_size; j++) - { - output_data[to_pos + j] += updates_data[i * slice_size + j]; - } - } -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const RuntimeShape &output_shape, SequentialTensorWriter *writer) -{ - const RuntimeShape ext_shape = RuntimeShape::ExtendedShape(5, input_shape); - TFLITE_DCHECK_LE(op_params.begin_count, 5); - TFLITE_DCHECK_LE(op_params.size_count, 5); - const int begin_count = op_params.begin_count; - const int size_count = op_params.size_count; - // We front-pad the begin and size vectors. - std::array start; - std::array stop; - for (int i = 0; i < 5; ++i) - { - int padded_i = 5 - i; - start[i] = begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; - stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) - ? ext_shape.Dims(i) - : start[i] + op_params.size[size_count - padded_i]; - } - - for (int i0 = start[0]; i0 < stop[0]; ++i0) - { - for (int i1 = start[1]; i1 < stop[1]; ++i1) - { - for (int i2 = start[2]; i2 < stop[2]; ++i2) - { - for (int i3 = start[3]; i3 < stop[3]; ++i3) - { - for (int i4 = start[4]; i4 < stop[4]; ++i4) - { - writer->Write(Offset(ext_shape, i0, i1, i2, i3, i4)); - } - } - } - } - } -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - SequentialTensorWriter writer(input_data, output_data); - return Slice(op_params, input_shape, output_shape, &writer); -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const TfLiteTensor *input, const RuntimeShape &output_shape, TfLiteTensor *output) -{ - SequentialTensorWriter writer(input, output); - return Slice(op_params, input_shape, output_shape, &writer); -} - -template -void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, output_shape); - - auto min_value = input2_data[0]; - for (int i = 0; i < flat_size; i++) - { - output_data[i] = input1_data[i] > min_value ? min_value : input1_data[i]; - } -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, - const T *input2_data, const RuntimeShape &output_shape, T *output_data) -{ - // Drop shape of second input: not needed. - Minimum(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, output_shape); - - auto max_value = input2_data[0]; - for (int i = 0; i < flat_size; i++) - { - output_data[i] = input1_data[i] < max_value ? max_value : input1_data[i]; - } -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, - const T *input2_data, const RuntimeShape &output_shape, T *output_data) -{ - // Drop shape of second input: not needed. - Maximum(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, const T3 *input2_data, - const RuntimeShape &output_shape, T2 *output_data) -{ - ArgMinMax(input1_shape, input1_data, input2_data, output_shape, output_data, std::greater()); -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, - const RuntimeShape &input2_shape, const T3 *input2_data, - const RuntimeShape &output_shape, T2 *output_data) -{ - // Drop shape of second input: not needed. - ArgMax(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void Select(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - int64_t flatsize; - // Allow select operator executions on mixed scalar tensors and one element - // tensors. - if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 && - input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) - { - flatsize = 1; - } - else - { - flatsize = MatchingFlatSize(input_condition_shape, input_x_shape, input_y_shape, output_shape); - } - for (int64_t i = 0; i < flatsize; ++i) - { - output_data[i] = input_condition_data[i] ? input_x_data[i] : input_y_data[i]; - } -} - -template -void RankOneSelect(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int64_t outer_size = input_condition_shape.FlatSize(); - int64_t inner_size; - if (input_condition_shape.DimensionsCount() == 0) - { - inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape); - } - else - { - TFLITE_DCHECK_EQ(MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0), outer_size); - inner_size = MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape); - } - - int64_t offset = 0; - for (int64_t i = 0; i < outer_size; i++) - { - const T *input_data = input_condition_data[i] ? input_x_data : input_y_data; - memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T)); - offset += inner_size; - } -} - -template -void BroadcastSelect4DSlow(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4); - - const RuntimeShape extended_output_shape = RuntimeShape::ExtendedShape(4, output_shape); - - NdArrayDesc<4> desc_condition; - NdArrayDesc<4> desc_x; - NdArrayDesc<4> desc_y; - NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape, input_y_shape, - &desc_condition, &desc_x, &desc_y); - - // In Tensorflow, the dimensions are canonically named (batch_number, row, - // col, channel), with extents (batches, height, width, depth), with the - // trailing dimension changing most rapidly (channels has the smallest - // stride, typically 1 element). - // - // In generated C code, we store arrays with the dimensions reversed. The - // first dimension has smallest stride. - // - // We name our variables by their Tensorflow convention, but generate C code - // nesting loops such that the innermost loop has the smallest stride for - // the best cache behavior. - for (int b = 0; b < extended_output_shape.Dims(0); ++b) - { - for (int y = 0; y < extended_output_shape.Dims(1); ++y) - { - for (int x = 0; x < extended_output_shape.Dims(2); ++x) - { - for (int c = 0; c < extended_output_shape.Dims(3); ++c) - { - const int condition_index = SubscriptToIndex(desc_condition, b, y, x, c); - const int x_index = SubscriptToIndex(desc_x, b, y, x, c); - const int y_index = SubscriptToIndex(desc_y, b, y, x, c); - output_data[Offset(extended_output_shape, b, y, x, c)] = - input_condition_data[condition_index] ? input_x_data[x_index] : input_y_data[y_index]; - } - } - } - } -} - -template -void SelectTrueCoords(const RuntimeShape &input_condition_shape, const D *input_condition_data, - T *output_data) -{ - const size_t size = input_condition_shape.FlatSize(); - if (size == 0) - { - // Dimension is zero, in which case we don't need to output. - return; - } - const size_t cond_rank = input_condition_shape.DimensionsCount(); - - std::vector dims_to_count(cond_rank, 0); - int cur_flat_size = size; - for (int i = 0; i < cond_rank; ++i) - { - dims_to_count[i] = cur_flat_size / input_condition_shape.Dims(i); - cur_flat_size = dims_to_count[i]; - } - - int output_index = 0; - for (int i = 0; i < size; ++i) - { - if (input_condition_data[i]) - { - // Insert the coordinate of the current item (row major) into output. - int flat_index = i; - for (int j = 0; j < cond_rank; ++j) - { - int coord_j = flat_index / dims_to_count[j]; - output_data[output_index * cond_rank + j] = coord_j; - flat_index %= dims_to_count[j]; - } - output_index++; - } - } -} - -// For easy implementation, the indices is always a vector of size-4 vectors. -template -inline void SparseToDense(const std::vector> &indices, const T *values, - T default_value, bool value_is_scalar, - const RuntimeShape &unextended_output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); - const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); - const int value_count = indices.size(); - - // First fill the output_data with default value. - const int num_elements = output_shape.FlatSize(); - for (int i = 0; i < num_elements; ++i) - { - output_data[i] = default_value; - } - - // Special handle for value is scalar case to avoid checking the boolean - // condition within the loop every time. - if (value_is_scalar) - { - for (int i = 0; i < value_count; ++i) - { - const std::vector &index = indices[i]; - TFLITE_DCHECK_EQ(index.size(), 4); - const T value = *values; // just use the first value. - output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; - } - return; - } - - // Go through the values and indices to fill the sparse values. - for (int i = 0; i < value_count; ++i) - { - const std::vector &index = indices[i]; - TFLITE_DCHECK_EQ(index.size(), 4); - const T value = values[i]; - output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; - } -} - -template -inline void Pow(const RuntimeShape &input1_shape, const T *input1_data, - const RuntimeShape &input2_shape, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, input2_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - output_data[i] = std::pow(input1_data[i], input2_data[i]); - } -} - -template -inline void BroadcastPow4DSlow(const RuntimeShape &unextended_input1_shape, const T *input1_data, - const RuntimeShape &unextended_input2_shape, const T *input2_data, - const RuntimeShape &unextended_output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); - const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); - - NdArrayDesc<4> desc1; - NdArrayDesc<4> desc2; - NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, - &desc2); - - for (int b = 0; b < output_shape.Dims(0); ++b) - { - for (int y = 0; y < output_shape.Dims(1); ++y) - { - for (int x = 0; x < output_shape.Dims(2); ++x) - { - for (int c = 0; c < output_shape.Dims(3); ++c) - { - auto out_idx = Offset(output_shape, b, y, x, c); - auto in1_idx = SubscriptToIndex(desc1, b, y, x, c); - auto in2_idx = SubscriptToIndex(desc2, b, y, x, c); - auto in1_val = input1_data[in1_idx]; - auto in2_val = input2_data[in2_idx]; - output_data[out_idx] = std::pow(in1_val, in2_val); - } - } - } - } -} - -template -void Reverse(int axis, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("Reverse"); - - int outer_size = 1; - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - - int copy_size = 1; - for (int i = axis + 1; i < input_shape.DimensionsCount(); ++i) - { - copy_size *= input_shape.Dims(i); - } - - const int dims_at_axis = input_shape.Dims(axis); - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_axis; ++j) - { - const int start_pos = (i * dims_at_axis + j) * copy_size; - Scalar *output_ptr = output_data + start_pos; - int loc = (i * dims_at_axis + dims_at_axis - j - 1) * copy_size; - memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); - } - } -} - -template -void ReverseSequence(const TS *seq_lengths, const int seq_dim, const int batch_dim, - const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("ReverseSequence"); - - int outer_size = 1; - int outer_dim = std::min(batch_dim, seq_dim); - int medium_dim = std::max(batch_dim, seq_dim); - for (int i = 0; i < outer_dim; ++i) - { - outer_size *= input_shape.Dims(i); - } - - int medium_size = 1; - for (int i = outer_dim + 1; i < medium_dim; ++i) - { - medium_size *= input_shape.Dims(i); - } - - int copy_size = 1; - for (int i = medium_dim + 1; i < input_shape.DimensionsCount(); ++i) - { - copy_size *= input_shape.Dims(i); - } - - const int dims_at_outer_dim = input_shape.Dims(outer_dim); - const int dims_at_medium_dim = input_shape.Dims(medium_dim); - - Scalar *output_ptr; - if (batch_dim > seq_dim) - { - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_outer_dim; ++j) - { - const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; - for (int p = 0; p < medium_size; ++p) - { - for (int q = 0; q < dims_at_medium_dim; ++q) - { - const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; - const Scalar *in_ptr = input_data + in_pos; - int sl = seq_lengths[q] - 1; - if (j > sl) - { - output_ptr = output_data + in_pos; - } - else - { - const int out_pos_base = (i * dims_at_outer_dim + sl - j) * medium_size; - const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + q) * copy_size; - output_ptr = output_data + out_pos; - } - memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); - } - } - } - } - } - else if (batch_dim < seq_dim) - { - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_outer_dim; ++j) - { - const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; - int sl = seq_lengths[j] - 1; - const int out_pos_base = (i * dims_at_outer_dim + j) * medium_size; - for (int p = 0; p < medium_size; ++p) - { - for (int q = 0; q < dims_at_medium_dim; ++q) - { - const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; - const Scalar *in_ptr = input_data + in_pos; - if (q > sl) - { - output_ptr = output_data + in_pos; - } - else - { - const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + sl - q) * copy_size; - output_ptr = output_data + out_pos; - } - memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); - } - } - } - } - } -} - -template -inline void SegmentSum(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &segment_ids_shape, const int32_t *segment_ids_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int segment_flat_size = MatchingFlatSizeSkipDim(input_shape, 0, output_shape); - - memset(output_data, 0, sizeof(T) * output_shape.FlatSize()); - - for (int i = 0; i < input_shape.Dims(0); i++) - { - int output_index = segment_ids_data[i]; - for (int j = 0; j < segment_flat_size; ++j) - { - output_data[output_index * segment_flat_size + j] += input_data[i * segment_flat_size + j]; - } - } -} - -} // namespace reference_ops -} // namespace tflite - -#endif // LUCI_INTERPRETER_PAL_REFERENCE_OPS_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALElu.h b/onert-micro/luci-interpreter/pal/common/PALAbs.h similarity index 54% rename from onert-micro/luci-interpreter/pal/cmsisnn/PALElu.h rename to onert-micro/luci-interpreter/pal/common/PALAbs.h index fc8c573..a1f5ae3 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALElu.h +++ b/onert-micro/luci-interpreter/pal/common/PALAbs.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +14,22 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_ELU_H -#define LUCI_INTERPRETER_PAL_ELU_H +#ifndef LUCI_INTERPRETER_PAL_ABS_H +#define LUCI_INTERPRETER_PAL_ABS_H -#include +#include namespace luci_interpreter_pal { -static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) +inline void Abs(const int flat_size, const float *input_data, float *output_data) { - tflite::reference_ops::Elu(input_shape, input_data, output_shape, output_data); + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = std::abs(input_data[i]); + } } } // namespace luci_interpreter_pal -#endif // LUCI_INTERPRETER_PAL_ELU_H +#endif // LUCI_INTERPRETER_PAL_ABS_H diff --git a/onert-micro/luci-interpreter/pal/common/PALAddCommon.h b/onert-micro/luci-interpreter/pal/common/PALAddCommon.h new file mode 100644 index 0000000..57f9b10 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALAddCommon.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_ADD_COMMON_H +#define LUCI_INTERPRETER_PAL_ADD_COMMON_H + +#include "Params.h" +#include "PALUtils.h" +#include "ProcessBroadcastShapes.h" + +namespace luci_interpreter_pal +{ + +// TODO: check if there real activation value +template +inline void Add(const ArithmeticParams ¶ms, const int flat_size, const T *input1_data, + const T *input2_data, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input1_data[i] + input2_data[i], activation_min), activation_max); +} + +template +inline void +BroadcastAdd4DSlow(const ArithmeticParams ¶ms, + const luci_interpreter::RuntimeShape &input1_shape, const T *input1_data, + const luci_interpreter::RuntimeShape &input2_shape, const T *input2_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, &desc2); + const luci_interpreter::RuntimeShape extended_output_shape = + luci_interpreter::RuntimeShape::extendedShape(4, output_shape); + + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < extended_output_shape.dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.dims(3); ++c) + { + const int output_data_offset = + ((b * extended_output_shape.dims(1) + y) * extended_output_shape.dims(2) + x) * + extended_output_shape.dims(3) + + c; + + output_data[output_data_offset] = + std::min(std::max(input1_data[subscriptToIndex(desc1, b, y, x, c)] + + input2_data[subscriptToIndex(desc2, b, y, x, c)], + activation_min), + activation_max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ADD_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALAddN.h b/onert-micro/luci-interpreter/pal/common/PALAddN.h new file mode 100644 index 0000000..63fbc03 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALAddN.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_ADD_N_H +#define LUCI_INTERPRETER_PAL_ADD_N_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +// T is expected to be either float or int. +template +inline void AddN(const size_t flat_size, const size_t num_inputs, const T *const *input_data, + T *output_data) +{ + // All inputs and output should have the same shape, this is checked during + // Prepare stage. + for (size_t i = 0; i < flat_size; ++i) + { + T x = 0; + for (size_t j = 0; j < num_inputs; ++j) + { + x += input_data[j][i]; + } + output_data[i] = x; + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ADD_N_H diff --git a/onert-micro/luci-interpreter/pal/common/PALArgMinMax.h b/onert-micro/luci-interpreter/pal/common/PALArgMinMax.h new file mode 100644 index 0000000..58602d8 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALArgMinMax.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_ARG_MIN_MAX_H +#define LUCI_INTERPRETER_PAL_ARG_MIN_MAX_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +template +void ArgMinMax(const luci_interpreter::RuntimeShape &input1_shape, const T1 *input1_data, + const T3 *input2_data, const luci_interpreter::RuntimeShape &output_shape, + T2 *output_data, const Cmp &cmp) +{ + int axis = input2_data[0]; + if (axis < 0) + { + axis += input1_shape.dimensionsCount(); + } + const int axis_size = input1_shape.dims(axis); + + int outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= input1_shape.dims(i); + } + + int inner_size = 1; + const int dims_count = input1_shape.dimensionsCount(); + for (int i = axis + 1; i < dims_count; ++i) + { + inner_size *= input1_shape.dims(i); + } + for (int outer = 0; outer < outer_size; ++outer) + { + for (int inner = 0; inner < inner_size; ++inner) + { + auto min_max_value = input1_data[outer * axis_size * inner_size + inner]; + T2 min_max_index = 0; + for (int i = 1; i < axis_size; ++i) + { + const auto &curr_value = input1_data[(outer * axis_size + i) * inner_size + inner]; + if (cmp(curr_value, min_max_value)) + { + min_max_value = curr_value; + min_max_index = static_cast(i); + } + } + output_data[outer * inner_size + inner] = min_max_index; + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ARG_MIN_MAX_H diff --git a/onert-micro/luci-interpreter/pal/common/PALAveragePool2DCommon.h b/onert-micro/luci-interpreter/pal/common/PALAveragePool2DCommon.h new file mode 100644 index 0000000..ec6bb55 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALAveragePool2DCommon.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_COMMON_H +#define LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_COMMON_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +// TODO: reduce code duplication with MaxPool +inline void AveragePool(const PoolParams ¶ms, const luci_interpreter::RuntimeShape &input_shape, + const float *input_data, const luci_interpreter::RuntimeShape &output_shape, + float *output_data) +{ + const int batches = input_shape.dims(0); + const int depth = output_shape.dims(3); + const int input_height = input_shape.dims(1); + const int input_width = input_shape.dims(2); + const int output_height = output_shape.dims(1); + const int output_width = output_shape.dims(2); + const int stride_height = params.stride_height; + const int stride_width = params.stride_width; + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - params.padding_values.width; + const int in_y_origin = (out_y * stride_height) - params.padding_values.height; + // Compute the boundaries of the filter region clamped so as to + // ensure that the filter window fits in the input array. + const int filter_x_start = std::max(0, -in_x_origin); + const int filter_x_end = std::min(params.filter_width, input_width - in_x_origin); + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(params.filter_height, input_height - in_y_origin); + + float total = 0.f; + float filter_count = 0; + + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (int filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const int in_x = in_x_origin + filter_x; + const int in_y = in_y_origin + filter_y; + + const int input_data_offset = + ((batch * input_shape.dims(1) + in_y) * input_shape.dims(2) + in_x) * + input_shape.dims(3) + + channel; + + total += input_data[input_data_offset]; + filter_count++; + } + } + const int output_data_offset = + ((batch * output_shape.dims(1) + out_y) * output_shape.dims(2) + out_x) * + output_shape.dims(3) + + channel; + + assert(filter_count != 0); + const float average = total / filter_count; + + output_data[output_data_offset] = + std::min(std::max(average, params.float_activation_min), params.float_activation_max); + } + } + } + } +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALComparisons.h b/onert-micro/luci-interpreter/pal/common/PALComparisons.h new file mode 100644 index 0000000..bb855a1 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALComparisons.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_COMPARISONS_H +#define LUCI_INTERPRETER_PAL_COMPARISONS_H + +#include "Params.h" +#include "ProcessBroadcastShapes.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ +namespace +{ + +struct BroadcastComparison4DSlowCommon +{ + const luci_interpreter::RuntimeShape output_shape; + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; +}; + +inline BroadcastComparison4DSlowCommon +BroadcastComparison4DSlowPreprocess(const luci_interpreter::RuntimeShape &unextended_input1_shape, + const luci_interpreter::RuntimeShape &unextended_input2_shape, + const luci_interpreter::RuntimeShape &unextended_output_shape) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, + &desc2); + return {luci_interpreter::RuntimeShape::extendedShape(4, unextended_output_shape), desc1, desc2}; +} + +} // namespace + +template inline bool LessFn(T lhs, T rhs) { return lhs < rhs; } +template inline bool LessEqualFn(T lhs, T rhs) { return lhs <= rhs; } +template inline bool EqualFn(T lhs, T rhs) { return lhs == rhs; } +template inline bool GreaterFn(T lhs, T rhs) { return lhs > rhs; } +template inline bool GreaterEqualFn(T lhs, T rhs) { return lhs >= rhs; } +template inline bool NotEqualFn(T lhs, T rhs) { return lhs != rhs; } + +template +inline void ComparisonNoScaling(const int64_t flat_size, const T *input1_data, const T *input2_data, + bool *output_data, bool F(T, T)) +{ + for (int64_t i = 0; i < flat_size; ++i) + { + output_data[i] = F(input1_data[i], input2_data[i]); + } +} + +template +inline void BroadcastComparison4DSlowWithScaling( + const ComparisonParams &op_params, const luci_interpreter::RuntimeShape &unextended_input1_shape, + const T *input1_data, const luci_interpreter::RuntimeShape &unextended_input2_shape, + const T *input2_data, const luci_interpreter::RuntimeShape &unextended_output_shape, + bool *output_data, bool F(T, T)) +{ + const BroadcastComparison4DSlowCommon dims = BroadcastComparison4DSlowPreprocess( + unextended_input1_shape, unextended_input2_shape, unextended_output_shape); + + int left_shift = op_params.left_shift; + int32_t input1_offset = op_params.input1_offset; + int32_t input1_multiplier = op_params.input1_multiplier; + int input1_shift = op_params.input1_shift; + int32_t input2_offset = op_params.input2_offset; + int32_t input2_multiplier = op_params.input2_multiplier; + int input2_shift = op_params.input2_shift; + + for (int b = 0; b < dims.output_shape.dims(0); ++b) + { + for (int y = 0; y < dims.output_shape.dims(1); ++y) + { + for (int x = 0; x < dims.output_shape.dims(2); ++x) + { + for (int c = 0; c < dims.output_shape.dims(3); ++c) + { + const int32_t input1_val = + input1_offset + input1_data[subscriptToIndex(dims.desc1, b, y, x, c)]; + const int32_t input2_val = + input2_offset + input2_data[subscriptToIndex(dims.desc2, b, y, x, c)]; + const int32_t shifted_input1_val = input1_val * (1 << left_shift); + const int32_t shifted_input2_val = input2_val * (1 << left_shift); + const int32_t scaled_input1_val = multiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input1_val, input1_multiplier, input1_shift); + const int32_t scaled_input2_val = multiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input2_val, input2_multiplier, input2_shift); + + const int output_data_offset = + ((b * dims.output_shape.dims(1) + y) * dims.output_shape.dims(2) + x) * + dims.output_shape.dims(3) + + c; + output_data[output_data_offset] = F(scaled_input1_val, scaled_input2_val); + } + } + } + } +} + +template +inline void ComparisonWithScaling(const ComparisonParams &op_params, const int64_t flat_size, + const T *input1_data, const T *input2_data, bool *output_data, + bool F(T, T)) +{ + int left_shift = op_params.left_shift; + int32_t input1_offset = op_params.input1_offset; + int32_t input1_multiplier = op_params.input1_multiplier; + int input1_shift = op_params.input1_shift; + int32_t input2_offset = op_params.input2_offset; + int32_t input2_multiplier = op_params.input2_multiplier; + int input2_shift = op_params.input2_shift; + + for (int64_t i = 0; i < flat_size; ++i) + { + const int32_t input1_val = input1_offset + input1_data[i]; + const int32_t input2_val = input2_offset + input2_data[i]; + const int32_t shifted_input1_val = input1_val * (1 << left_shift); + const int32_t shifted_input2_val = input2_val * (1 << left_shift); + const int32_t scaled_input1_val = multiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input1_val, input1_multiplier, input1_shift); + const int32_t scaled_input2_val = multiplyByQuantizedMultiplierSmallerThanOneExp( + shifted_input2_val, input2_multiplier, input2_shift); + output_data[i] = F(scaled_input1_val, scaled_input2_val); + } +} + +template +inline void BroadcastComparison4DSlowNoScaling( + const ComparisonParams &op_params, const luci_interpreter::RuntimeShape &unextended_input1_shape, + const T *input1_data, const luci_interpreter::RuntimeShape &unextended_input2_shape, + const T *input2_data, const luci_interpreter::RuntimeShape &unextended_output_shape, + bool *output_data, bool F(T, T)) +{ + const BroadcastComparison4DSlowCommon dims = BroadcastComparison4DSlowPreprocess( + unextended_input1_shape, unextended_input2_shape, unextended_output_shape); + + for (int b = 0; b < dims.output_shape.dims(0); ++b) + { + for (int y = 0; y < dims.output_shape.dims(1); ++y) + { + for (int x = 0; x < dims.output_shape.dims(2); ++x) + { + for (int c = 0; c < dims.output_shape.dims(3); ++c) + { + const int output_data_offset = + ((b * dims.output_shape.dims(1) + y) * dims.output_shape.dims(2) + x) * + dims.output_shape.dims(3) + + c; + output_data[output_data_offset] = + F(input1_data[subscriptToIndex(dims.desc1, b, y, x, c)], + input2_data[subscriptToIndex(dims.desc2, b, y, x, c)]); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_COMPARISONS_H diff --git a/onert-micro/luci-interpreter/pal/common/PALConcatenation.h b/onert-micro/luci-interpreter/pal/common/PALConcatenation.h new file mode 100644 index 0000000..2bd3858 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALConcatenation.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_CONCATENATION_H +#define LUCI_INTERPRETER_PAL_CONCATENATION_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +template +inline void Concatenation(const ConcatenationParams ¶ms, + const luci_interpreter::RuntimeShape *const *input_shapes, + const Scalar *const *input_data, + const luci_interpreter::RuntimeShape &output_shape, Scalar *output_data) +{ + int axis = params.axis; + int inputs_count = params.inputs_count; + const int concat_dimensions = output_shape.dimensionsCount(); + + int64_t concat_size = 0; + for (int i = 0; i < inputs_count; i++) + { + concat_size += input_shapes[i]->dims(axis); + } + int64_t outer_size = 1; + for (int i = 0; i < axis; ++i) + { + outer_size *= output_shape.dims(i); + } + // For all input arrays, + // FlatSize() = outer_size * Dims(axis) * base_inner_size; + int64_t base_inner_size = 1; + for (int i = axis + 1; i < concat_dimensions; ++i) + { + base_inner_size *= output_shape.dims(i); + } + + Scalar *output_ptr = output_data; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < inputs_count; ++i) + { + const int copy_size = input_shapes[i]->dims(axis) * base_inner_size; + const Scalar *input_ptr = input_data[i] + k * copy_size; + memcpy(output_ptr, input_ptr, copy_size * sizeof(Scalar)); + output_ptr += copy_size; + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_CONCATENATION_H diff --git a/onert-micro/luci-interpreter/pal/common/PALConv2DCommon.h b/onert-micro/luci-interpreter/pal/common/PALConv2DCommon.h new file mode 100644 index 0000000..04b92cd --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALConv2DCommon.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_CONV2D_COMMON_H +#define LUCI_INTERPRETER_PAL_CONV2D_COMMON_H +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ +static inline void Conv(const ConvParams ¶ms, const int32_t *input_shape, + const float *input_data, const int32_t *filter_shape, + const float *filter_data, const float *bias_data, + const int32_t *output_shape, float *output_data) +{ + const int stride_width = params.stride_width; + const int stride_height = params.stride_height; + const int dilation_width_factor = params.dilation_width_factor; + const int dilation_height_factor = params.dilation_height_factor; + const int pad_width = params.padding_values.width; + const int pad_height = params.padding_values.height; + const float output_activation_min = params.float_activation_min; + const float output_activation_max = params.float_activation_max; + + const auto batches = input_shape[0]; + const int input_height = input_shape[1]; + const int input_width = input_shape[2]; + const int input_depth = input_shape[3]; + const int output_depth = filter_shape[0]; + const int filter_height = filter_shape[1]; + const int filter_width = filter_shape[2]; + const int output_height = output_shape[1]; + const int output_width = output_shape[2]; + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + const int in_y_origin = (out_y * stride_height) - pad_height; + for (int out_x = 0; out_x < output_width; ++out_x) + { + const int in_x_origin = (out_x * stride_width) - pad_width; + for (int out_channel = 0; out_channel < output_depth; ++out_channel) + { + float total = 0.f; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + const int in_y = in_y_origin + dilation_height_factor * filter_y; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + + // Zero padding by omitting the areas outside the image. + const bool is_point_inside_image = + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + + if (!is_point_inside_image) + { + continue; + } + + for (int in_channel = 0; in_channel < input_depth; ++in_channel) + { + const int input_data_offset = + ((batch * input_height + in_y) * input_width + in_x) * input_depth + in_channel; + + const int filter_data_offset = + ((out_channel * filter_height + filter_y) * filter_width + filter_x) * + input_depth + + in_channel; + + const float input_value = input_data[input_data_offset]; + const float filter_value = filter_data[filter_data_offset]; + total += (input_value * filter_value); + } + } + } + // float bias_value = 0.0f; + if (bias_data) + { + total += bias_data[out_channel]; + } + + const int output_data_offset = + ((batch * output_height + out_y) * output_width + out_x) * output_depth + out_channel; + + output_data[output_data_offset] = + std::min(std::max(total, output_activation_min), output_activation_max); + } + } + } + } +} + +static inline void Conv(const ConvParams ¶ms, const int32_t *input_shape, + const uint8_t *input_data, const int32_t *filter_shape, + const uint8_t *filter_data, const int32_t *bias_data, + const int32_t *output_shape, uint8_t *output_data) +{ + const int stride_width = params.stride_width; + const int stride_height = params.stride_height; + const int dilation_width_factor = params.dilation_width_factor; + const int dilation_height_factor = params.dilation_height_factor; + const int pad_width = params.padding_values.width; + const int pad_height = params.padding_values.height; + const int32_t input_offset = params.input_offset; + const int32_t filter_offset = params.weights_offset; + const int32_t output_offset = params.output_offset; + const int32_t output_multiplier = params.output_multiplier; + const int output_shift = params.output_shift; + const int32_t output_activation_min = params.quantized_activation_min; + const int32_t output_activation_max = params.quantized_activation_max; + + const auto batches = input_shape[0]; + const int input_height = input_shape[1]; + const int input_width = input_shape[2]; + const int input_depth = input_shape[3]; + const int output_depth = filter_shape[0]; + const int filter_height = filter_shape[1]; + const int filter_width = filter_shape[2]; + const int output_height = output_shape[1]; + const int output_width = output_shape[2]; + + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + const int in_y_origin = (out_y * stride_height) - pad_height; + for (int out_x = 0; out_x < output_width; ++out_x) + { + const int in_x_origin = (out_x * stride_width) - pad_width; + for (int out_channel = 0; out_channel < output_depth; ++out_channel) + { + int32_t acc = 0; + for (int filter_y = 0; filter_y < filter_height; ++filter_y) + { + const int in_y = in_y_origin + dilation_height_factor * filter_y; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int in_x = in_x_origin + dilation_width_factor * filter_x; + + // Zero padding by omitting the areas outside the image. + const bool is_point_inside_image = + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + + if (!is_point_inside_image) + { + continue; + } + + for (int in_channel = 0; in_channel < input_depth; ++in_channel) + { + const int input_data_offset = + ((batch * input_height + in_y) * input_width + in_x) * input_depth + in_channel; + + const int filter_data_offset = + ((out_channel * filter_height + filter_y) * filter_width + filter_x) * + input_depth + + in_channel; + + const int32_t input_val = input_data[input_data_offset]; + const int32_t filter_val = filter_data[filter_data_offset]; + acc += (filter_val + filter_offset) * (input_val + input_offset); + } + } + } + if (bias_data) + { + acc += bias_data[out_channel]; + } + acc = multiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + acc += output_offset; + acc = std::max(acc, output_activation_min); + acc = std::min(acc, output_activation_max); + + const int output_data_offset = + ((batch * output_height + out_y) * output_width + out_x) * output_depth + out_channel; + + output_data[output_data_offset] = static_cast(acc); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_CONV2D_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALDiv.h b/onert-micro/luci-interpreter/pal/common/PALDiv.h new file mode 100644 index 0000000..cca85cd --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALDiv.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_DIV_COMMON_H +#define LUCI_INTERPRETER_PAL_DIV_COMMON_H + +#include "Params.h" +#include "PALUtils.h" +#include "ProcessBroadcastShapes.h" + +namespace luci_interpreter_pal +{ +template +inline void Div(const ArithmeticParams ¶ms, const int flat_size, const T *input1_data, + const T *input2_data, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input1_data[i] / input2_data[i], activation_min), activation_max); +} + +template +inline void DivScalar(const ArithmeticParams ¶ms, const int flat_size, const T *input_data, + const T scalar_value, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input_data[i] / scalar_value, activation_min), activation_max); +} + +template +inline void +BroadcastDiv4DSlow(const ArithmeticParams ¶ms, + const luci_interpreter::RuntimeShape &input1_shape, const T *input1_data, + const luci_interpreter::RuntimeShape &input2_shape, const T *input2_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = input1_shape.flatSize(); + + if (params.broadcast_category == BroadcastableOpCategory::kScalarFirstBroadcast) + { + return DivScalar(params, flat_size, input2_data, input1_data[0], output_data); + } + else if (params.broadcast_category == BroadcastableOpCategory::kScalarSecondBroadcast) + { + return DivScalar(params, flat_size, input1_data, input2_data[0], output_data); + } + + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, &desc2); + const luci_interpreter::RuntimeShape extended_output_shape = + luci_interpreter::RuntimeShape::extendedShape(4, output_shape); + + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < extended_output_shape.dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.dims(3); ++c) + { + const int output_data_offset = + ((b * extended_output_shape.dims(1) + y) * extended_output_shape.dims(2) + x) * + extended_output_shape.dims(3) + + c; + + output_data[output_data_offset] = + std::min(std::max(input1_data[subscriptToIndex(desc1, b, y, x, c)] / + input2_data[subscriptToIndex(desc2, b, y, x, c)], + activation_min), + activation_max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_DIV_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALElu.h b/onert-micro/luci-interpreter/pal/common/PALElu.h similarity index 63% rename from onert-micro/luci-interpreter/pal/mcu/PALElu.h rename to onert-micro/luci-interpreter/pal/common/PALElu.h index fc8c573..661bd07 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALElu.h +++ b/onert-micro/luci-interpreter/pal/common/PALElu.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 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. @@ -18,15 +18,20 @@ #ifndef LUCI_INTERPRETER_PAL_ELU_H #define LUCI_INTERPRETER_PAL_ELU_H -#include +#include "PALUtils.h" +#include namespace luci_interpreter_pal { -static inline void Elu(const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) +inline void Elu(const int flat_size, const float *input_data, float *output_data) { - tflite::reference_ops::Elu(input_shape, input_data, output_shape, output_data); + for (int i = 0; i < flat_size; i++) + { + float val = input_data[i]; + float result = val < 0.0f ? std::expm1(val) : val; + output_data[i] = result; + } } } // namespace luci_interpreter_pal diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/PALNeg.h b/onert-micro/luci-interpreter/pal/common/PALExp.h similarity index 53% rename from onert-micro/luci-interpreter/pal/cmsisnn/PALNeg.h rename to onert-micro/luci-interpreter/pal/common/PALExp.h index b4e430e..5cf1ef6 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/PALNeg.h +++ b/onert-micro/luci-interpreter/pal/common/PALExp.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 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. @@ -15,19 +15,24 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_NEG_H -#define LUCI_INTERPRETER_PAL_NEG_H +#ifndef LUCI_INTERPRETER_PAL_EXP_H +#define LUCI_INTERPRETER_PAL_EXP_H -#include +#include "PALUtils.h" namespace luci_interpreter_pal { -template -static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) + +inline void Exp(const int flat_size, const float *input_data, float *output_data) { - tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); + for (int i = 0; i < flat_size; i++) + { + const float val = input_data[i]; + const float result = std::exp(val); + output_data[i] = result; + } } + } // namespace luci_interpreter_pal -#endif // LUCI_INTERPRETER_PAL_NEG_H +#endif // LUCI_INTERPRETER_PAL_EXP_H diff --git a/onert-micro/luci-interpreter/pal/common/PALFullyConnectedCommon.h b/onert-micro/luci-interpreter/pal/common/PALFullyConnectedCommon.h new file mode 100644 index 0000000..14934cc --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALFullyConnectedCommon.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_FULLY_CONNECTED_COMMON_H +#define LUCI_INTERPRETER_PAL_FULLY_CONNECTED_COMMON_H + +#include "PALUtils.h" +#include "Params.h" + +namespace luci_interpreter_pal +{ + +template +inline void FullyConnected(const FullyConnectedParams ¶ms, const int32_t *input_shape, + const InputType *input_data, const int32_t *filter_shape, + const WeightType *filter_data, const BiasType *bias_data, + const int32_t *output_shape, OutputType *output_data) +{ + const int32_t input_offset = params.input_offset; + const int32_t filter_offset = params.weights_offset; + const int32_t output_offset = params.output_offset; + const int32_t output_multiplier = params.output_multiplier; + const int output_shift = params.output_shift; + const int32_t output_activation_min = params.quantized_activation_min; + const int32_t output_activation_max = params.quantized_activation_max; + + const int batches = input_shape[0]; + const int output_depth = output_shape[1]; + const int accum_depth = filter_shape[1]; + + for (int b = 0; b < batches; ++b) + { + for (int out_c = 0; out_c < output_depth; ++out_c) + { + BiasType acc = 0; + for (int d = 0; d < accum_depth; ++d) + { + int32_t input_val = input_data[b * accum_depth + d]; + int32_t filter_val = filter_data[out_c * accum_depth + d]; + acc += (filter_val + filter_offset) * (input_val + input_offset); + } + if (bias_data) + { + acc += bias_data[out_c]; + } + int32_t acc_scaled = multiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + acc_scaled += output_offset; + acc_scaled = std::max(acc_scaled, output_activation_min); + acc_scaled = std::min(acc_scaled, output_activation_max); + output_data[out_c + output_depth * b] = static_cast(acc_scaled); + } + } +} +template <> +inline void FullyConnected(const FullyConnectedParams ¶ms, const int32_t *input_shape, + const float *input_data, const int32_t *filter_shape, + const float *filter_data, const float *bias_data, + const int32_t *output_shape, float *output_data) +{ + const float output_activation_min = params.float_activation_min; + const float output_activation_max = params.float_activation_max; + + const int batches = input_shape[0]; + const int output_depth = output_shape[1]; + const int accum_depth = filter_shape[1]; + + for (int b = 0; b < batches; ++b) + { + for (int out_c = 0; out_c < output_depth; ++out_c) + { + float total = 0.f; + for (int d = 0; d < accum_depth; ++d) + { + total += input_data[b * accum_depth + d] * filter_data[out_c * accum_depth + d]; + } + float bias_value = 0.0f; + if (bias_data) + { + bias_value = bias_data[out_c]; + } + output_data[out_c + output_depth * b] = + std::min(std::max(total + bias_value, output_activation_min), output_activation_max); + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_FULLY_CONNECTED_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALLogicalCommon.h b/onert-micro/luci-interpreter/pal/common/PALLogicalCommon.h new file mode 100644 index 0000000..18173f5 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALLogicalCommon.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_LOGICAL_COMMON_H +#define LUCI_INTERPRETER_PAL_LOGICAL_COMMON_H + +namespace luci_interpreter_pal +{ + +inline void LogicalCommon(const int flat_size, const bool *input1_data, const bool *input2_data, + bool *output_data, bool (*f)(bool, bool)) +{ + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = f(input1_data[i], input2_data[i]); + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LOGICAL_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALLogistic.h b/onert-micro/luci-interpreter/pal/common/PALLogistic.h new file mode 100644 index 0000000..c0e3a3c --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALLogistic.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_LOGISTIC_H +#define LUCI_INTERPRETER_PAL_LOGISTIC_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +inline void Logistic(const int flat_size, const float *input_data, float *output_data) +{ + const float cutoff_upper = 16.619047164916992188f; + const float cutoff_lower = -9.f; + + // Rational for using approximation in reference kernel. + // 0. This approximation gives enough precision for float. + // 1. This works around an issue on an embedded chipset where exp() does not + // return correctly as expected - exp(x) should return inf when overflown + // not 1.701417 IEEE 754 defines representation for inf. + // 2. This will speed up calculation and is matching the behavior in the + // optimized kernels. (check the definition of scalar_logistic_op) + + for (int i = 0; i < flat_size; i++) + { + float val = input_data[i]; + float result; + if (val > cutoff_upper) + { + result = 1.0f; + } + else if (val < cutoff_lower) + { + result = std::exp(val); + } + else + { + result = 1.f / (1.f + std::exp(-val)); + } + output_data[i] = result; + } +} + +inline void Logistic(const int flat_size, const int8_t *input_data, float input_scale, + int input_zero_point, int8_t *output_data, float output_scale, + int output_zero_point) +{ + const float cutoff_upper = 16.619047164916992188f; + const float cutoff_lower = -9.f; + + // Rational for using approximation in reference kernel. + // 0. This approximation gives enough precision for float. + // 1. This works around an issue on an embedded chipset where exp() does not + // return correctly as expected - exp(x) should return inf when overflown + // not 1.701417 IEEE 754 defines representation for inf. + // 2. This will speed up calculation and is matching the behavior in the + // optimized kernels. (check the definition of scalar_logistic_op) + + for (int i = 0; i < flat_size; i++) + { + // Dequantize. + float val = static_cast((input_data[i] - input_zero_point) * input_scale); + float result; + if (val > cutoff_upper) + { + result = 1.0f; + } + else if (val < cutoff_lower) + { + result = std::exp(val); + } + else + { + result = 1.f / (1.f + std::exp(-val)); + } + // Requantize + int8_t output = static_cast(result / output_scale + output_zero_point); + output_data[i] = output; + } +} + +inline void Logistic(int32_t input_multiplier, int32_t input_left_shift, int32_t input_size, + const int16_t *ptr_input_data, int16_t *ptr_output_data) +{ + // We use the LUT for sigmoid and take into account, that + // tanh(x) = 2*sigmoid(2*x) - 1 + + // We scale by 3/4 to expand range [-8,8]->[-10.7,10.7]. + // In case of general parameter scale, multiplier 3 is taken into account + // in TanhPrepare function and it is included in + // input_multiplier already. + if (input_multiplier == 0) + { // power of two case + input_multiplier = 3 << input_left_shift; + input_left_shift = 0; + } + + int32_t round = (input_left_shift > 0) ? 1 << (input_left_shift - 1) : 0; + + for (int i = 0; i < input_size; ++i, ptr_input_data++, ptr_output_data++) + { + int32_t input_data = ((*ptr_input_data) * input_multiplier + round) >> input_left_shift; + + // We do interpolation on unsigned values. + uint32_t abs_input_data = abs(input_data); + + // We divide by 2 power of 9, because + // we need to divide by 2 in power of 7 for + // the input conversion + 1/4 from the scale above. + + // Define uh as uint32_t type not to make this function overflow. + uint32_t uh = abs_input_data >> 9; + uint32_t result; + + if (uh >= 255) + { + // Saturate to maximum. + result = 0x7FFF << 10; + } + else + { + uint32_t ua = sigmoid_table_uint16[uh]; + uint32_t ub = sigmoid_table_uint16[uh + 1]; + uint32_t ut = abs_input_data & 0x1ff; + // Interpolation is done using the fractional bit. + result = (ua << 9) + ut * (ub - ua); + } + + result = (input_data >= 0) ? (result + (1 << 9)) : ((1 << (16 + 9)) - result + (1 << 9) - 1); + + // Back to 16-bit. + result >>= 10; + + *ptr_output_data = result; + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_LOGISTIC_H diff --git a/onert-micro/luci-interpreter/pal/common/PALMaxPool2DCommon.h b/onert-micro/luci-interpreter/pal/common/PALMaxPool2DCommon.h new file mode 100644 index 0000000..034319b --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALMaxPool2DCommon.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_MAX_POOL_2D_COMMON_H +#define LUCI_INTERPRETER_PAL_MAX_POOL_2D_COMMON_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +inline void MaxPool(const PoolParams ¶ms, const luci_interpreter::RuntimeShape &input_shape, + const float *input_data, const luci_interpreter::RuntimeShape &output_shape, + float *output_data) +{ + const int batches = input_shape.dims(0); + const int depth = output_shape.dims(3); + const int input_height = input_shape.dims(1); + const int input_width = input_shape.dims(2); + const int output_height = output_shape.dims(1); + const int output_width = output_shape.dims(2); + const int stride_height = params.stride_height; + const int stride_width = params.stride_width; + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - params.padding_values.width; + const int in_y_origin = (out_y * stride_height) - params.padding_values.height; + // Compute the boundaries of the filter region clamped so as to + // ensure that the filter window fits in the input array. + const int filter_x_start = std::max(0, -in_x_origin); + const int filter_x_end = std::min(params.filter_width, input_width - in_x_origin); + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(params.filter_height, input_height - in_y_origin); + float max = std::numeric_limits::lowest(); + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (int filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const int in_x = in_x_origin + filter_x; + const int in_y = in_y_origin + filter_y; + + const int input_data_offset = + ((batch * input_shape.dims(1) + in_y) * input_shape.dims(2) + in_x) * + input_shape.dims(3) + + channel; + + max = std::max(max, input_data[input_data_offset]); + } + } + const int output_data_offset = + ((batch * output_shape.dims(1) + out_y) * output_shape.dims(2) + out_x) * + output_shape.dims(3) + + channel; + + output_data[output_data_offset] = + std::min(std::max(max, params.float_activation_min), params.float_activation_max); + } + } + } + } +} + +template +inline void MaxPool(const PoolParams ¶ms, const luci_interpreter::RuntimeShape &input_shape, + const T *input_data, const luci_interpreter::RuntimeShape &output_shape, + T *output_data) +{ + const int batches = input_shape.dims(0); + const int depth = output_shape.dims(3); + const int input_height = input_shape.dims(1); + const int input_width = input_shape.dims(2); + const int output_height = output_shape.dims(1); + const int output_width = output_shape.dims(2); + const int stride_height = params.stride_height; + const int stride_width = params.stride_width; + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - params.padding_values.width; + const int in_y_origin = (out_y * stride_height) - params.padding_values.height; + // Compute the boundaries of the filter region clamped so as to + // ensure that the filter window fits in the input array. + const int filter_x_start = std::max(0, -in_x_origin); + const int filter_x_end = std::min(params.filter_width, input_width - in_x_origin); + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(params.filter_height, input_height - in_y_origin); + T max = std::numeric_limits::lowest(); + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (int filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const int in_x = in_x_origin + filter_x; + const int in_y = in_y_origin + filter_y; + + const int input_data_offset = + ((batch * input_shape.dims(1) + in_y) * input_shape.dims(2) + in_x) * + input_shape.dims(3) + + channel; + + max = std::max(max, input_data[input_data_offset]); + } + } + max = std::max(max, params.quantized_activation_min); + max = std::min(max, params.quantized_activation_max); + + const int output_data_offset = + ((batch * output_shape.dims(1) + out_y) * output_shape.dims(2) + out_x) * + output_shape.dims(3) + + channel; + + output_data[output_data_offset] = static_cast(max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MAX_POOL_2D_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALMulCommon.h b/onert-micro/luci-interpreter/pal/common/PALMulCommon.h new file mode 100644 index 0000000..f171040 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALMulCommon.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_MUL_COMMON_H +#define LUCI_INTERPRETER_PAL_MUL_COMMON_H + +#include "Params.h" +#include "PALUtils.h" +#include "ProcessBroadcastShapes.h" + +namespace luci_interpreter_pal +{ +template +inline void Mul(const ArithmeticParams ¶ms, const int flat_size, const T *input1_data, + const T *input2_data, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input1_data[i] * input2_data[i], activation_min), activation_max); +} + +template +inline void MulScalar(const ArithmeticParams ¶ms, const int flat_size, const T *input_data, + const T scalar_value, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input_data[i] * scalar_value, activation_min), activation_max); +} + +template +inline void +BroadcastMul4DSlow(const ArithmeticParams ¶ms, + const luci_interpreter::RuntimeShape &input1_shape, const T *input1_data, + const luci_interpreter::RuntimeShape &input2_shape, const T *input2_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) +{ + const int flat_size = input1_shape.flatSize(); + + if (params.broadcast_category == BroadcastableOpCategory::kScalarFirstBroadcast) + { + return MulScalar(params, flat_size, input2_data, input1_data[0], output_data); + } + else if (params.broadcast_category == BroadcastableOpCategory::kScalarSecondBroadcast) + { + return MulScalar(params, flat_size, input1_data, input2_data[0], output_data); + } + + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, &desc2); + const luci_interpreter::RuntimeShape extended_output_shape = + luci_interpreter::RuntimeShape::extendedShape(4, output_shape); + + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < extended_output_shape.dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.dims(3); ++c) + { + const int output_data_offset = + ((b * extended_output_shape.dims(1) + y) * extended_output_shape.dims(2) + x) * + extended_output_shape.dims(3) + + c; + + output_data[output_data_offset] = + std::min(std::max(input1_data[subscriptToIndex(desc1, b, y, x, c)] * + input2_data[subscriptToIndex(desc2, b, y, x, c)], + activation_min), + activation_max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALNeg.h b/onert-micro/luci-interpreter/pal/common/PALNeg.h similarity index 53% rename from onert-micro/luci-interpreter/pal/linux/PALNeg.h rename to onert-micro/luci-interpreter/pal/common/PALNeg.h index c88b045..4c91538 100644 --- a/onert-micro/luci-interpreter/pal/linux/PALNeg.h +++ b/onert-micro/luci-interpreter/pal/common/PALNeg.h @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +17,29 @@ #ifndef LUCI_INTERPRETER_PAL_NEG_H #define LUCI_INTERPRETER_PAL_NEG_H -#include - namespace luci_interpreter_pal { template -static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) +inline void Negate(const luci_interpreter::RuntimeShape &input_shape, const T *input_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) + { - tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); + // check that input and output dimensions are equal + int N = input_shape.dimensionsCount(); + assert(N == output_shape.dimensionsCount()); + + // check that sizes of all dimensions are equal + for (int i = 0; i < N; ++i) + { + assert(input_shape.dims(i) == output_shape.dims(i)); + } + + const int flat_size = input_shape.flatSize(); + + for (int i = 0; i < flat_size; ++i) + { + output_data[i] = -input_data[i]; + } } } // namespace luci_interpreter_pal diff --git a/onert-micro/luci-interpreter/pal/common/PALPad.h b/onert-micro/luci-interpreter/pal/common/PALPad.h new file mode 100644 index 0000000..f9dd73f --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALPad.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_PAD_H +#define LUCI_INTERPRETER_PAL_PAD_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +constexpr int PadKernelMaxDimensionCount() { return 5; } + +void Pad(const PadParams &op_params, const luci_interpreter::RuntimeShape &input_shape, + const float *input_data, const float *pad_value_ptr, + const luci_interpreter::RuntimeShape &output_shape, float *output_data) +{ + // Runtime calls are currently fixed at 5 dimensions. Copy inputs so we can + // pad them to 5 dims (yes, we are "padding the padding"). + int left_padding_copy[PadKernelMaxDimensionCount()]; + for (int i = 0; i < PadKernelMaxDimensionCount(); i++) + { + left_padding_copy[i] = 0; + } + for (int i = 0; i < op_params.left_padding_count; ++i) + { + left_padding_copy[i + PadKernelMaxDimensionCount() - op_params.left_padding_count] = + op_params.left_padding[i]; + } + int right_padding_copy[PadKernelMaxDimensionCount()]; + for (int i = 0; i < PadKernelMaxDimensionCount(); i++) + { + right_padding_copy[i] = 0; + } + for (int i = 0; i < op_params.right_padding_count; ++i) + { + right_padding_copy[i + PadKernelMaxDimensionCount() - op_params.right_padding_count] = + op_params.right_padding[i]; + } + const auto extended_output = + luci_interpreter::RuntimeShape::extendedShape(PadKernelMaxDimensionCount(), output_shape); + const int output_batch = extended_output.dims(0); + const int output_plane = extended_output.dims(1); + const int output_height = extended_output.dims(2); + const int output_width = extended_output.dims(3); + const int output_depth = extended_output.dims(4); + + const int left_b_padding = left_padding_copy[0]; + const int left_p_padding = left_padding_copy[1]; + const int left_h_padding = left_padding_copy[2]; + const int left_w_padding = left_padding_copy[3]; + const int left_d_padding = left_padding_copy[4]; + + const int right_b_padding = right_padding_copy[0]; + const int right_p_padding = right_padding_copy[1]; + const int right_h_padding = right_padding_copy[2]; + const int right_w_padding = right_padding_copy[3]; + const int right_d_padding = right_padding_copy[4]; + + const float pad_value = *pad_value_ptr; + + const float *in_ptr = input_data; + float *out_ptr = output_data; + for (int out_b = 0; out_b < output_batch; ++out_b) + { + for (int out_p = 0; out_p < output_plane; ++out_p) + { + for (int out_h = 0; out_h < output_height; ++out_h) + { + for (int out_w = 0; out_w < output_width; ++out_w) + { + for (int out_d = 0; out_d < output_depth; ++out_d) + { + if (out_b < left_b_padding || out_b >= output_batch - right_b_padding || + out_p < left_p_padding || out_p >= output_plane - right_p_padding || + out_h < left_h_padding || out_h >= output_height - right_h_padding || + out_w < left_w_padding || out_w >= output_width - right_w_padding || + out_d < left_d_padding || out_d >= output_depth - right_d_padding) + { + *out_ptr++ = pad_value; + } + else + { + *out_ptr++ = *in_ptr++; + } + } + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_PAD_H diff --git a/onert-micro/luci-interpreter/pal/common/PALReduceCommon.h b/onert-micro/luci-interpreter/pal/common/PALReduceCommon.h new file mode 100644 index 0000000..a5b0e10 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALReduceCommon.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_TANH_H +#define LUCI_INTERPRETER_PAL_TANH_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ +namespace +{ +// This method parses the input 'axis' to remove duplicates and handle negative +// values, and returns a valid 'out_axis' +inline bool resolveAxis(const int num_dims, const int *axis, const int64_t num_axis, + int *out_num_axis) +{ + int out_axis[2]; + *out_num_axis = 0; // Just in case. + // Short-circuit axis resolution for scalars; the axis will go unused. + if (num_dims == 0) + { + return true; + } + // o(n^2) is fine since out_num_axis should be really small, mostly <= 4 + for (int64_t idx = 0; idx < num_axis; ++idx) + { + // Handle negative index. A positive index 'p_idx' can be represented as a + // negative index 'n_idx' as: n_idx = p_idx-num_dims + // eg: For num_dims=3, [0, 1, 2] is the same as [-3, -2, -1] */ + int current = axis[idx] < 0 ? (axis[idx] + num_dims) : axis[idx]; + if (current < 0 || current >= num_dims) + { + return false; + } + bool is_dup = false; + for (int j = 0; j < *out_num_axis; ++j) + { + if (out_axis[j] == current) + { + is_dup = true; + break; + } + } + if (!is_dup) + { + out_axis[*out_num_axis] = current; + *out_num_axis += 1; + } + } + return true; +} + +} // namespace + +// Computes the generic value (i.e., sum/max/min/prod) of elements across +// dimensions given in axis. It needs to pass in init_value and reducer. +template +inline void ReduceGeneric(const T *input_data, const int *input_dims, const int input_num_dims, + T *output_data, const int *axis, const int64_t num_axis_dimensions, + T init_value, const int output_flat_size, T reducer(const T, const T)) +{ + // Return early when input shape has zero dim. + for (int i = 0; i < input_num_dims; ++i) + { + if (input_dims[i] == 0) + return; + } + + for (size_t idx = 0; idx < output_flat_size; ++idx) + { + output_data[idx] = init_value; + } + + // Resolve axis. + int num_resolved_axis = 0; + if (!resolveAxis(input_num_dims, axis, num_axis_dimensions, &num_resolved_axis)) + { + return; + } + + int temp_index[5]; + // Reset input iterator. + for (int idx = 0; idx < input_num_dims; ++idx) + { + temp_index[idx] = 0; + } + // Iterate through input_data. + do + { + size_t input_offset = reducedOutputOffset(input_num_dims, input_dims, temp_index, 0, nullptr); + size_t output_offset = + reducedOutputOffset(input_num_dims, input_dims, temp_index, num_resolved_axis, axis); + output_data[output_offset] = reducer(output_data[output_offset], input_data[input_offset]); + } while (nextIndex(input_num_dims, input_dims, temp_index)); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_TANH_H diff --git a/onert-micro/luci-interpreter/pal/common/PALReluCommon.h b/onert-micro/luci-interpreter/pal/common/PALReluCommon.h new file mode 100644 index 0000000..260586a --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALReluCommon.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_RELU_COMMON_H +#define LUCI_INTERPRETER_PAL_RELU_COMMON_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +inline void ReLUCommon(const int flat_size, const float *input_data, float *output_data, + const float alpha, const bool is_relu_6) +{ + const float relu_6_value = 6.0f; + for (int i = 0; i < flat_size; i++) + { + const float val = input_data[i]; + float result = val > 0 ? val : val * alpha; + result = is_relu_6 ? (result > relu_6_value ? relu_6_value : result) : result; + output_data[i] = result; + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RELU_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALResizeBilinear.h b/onert-micro/luci-interpreter/pal/common/PALResizeBilinear.h new file mode 100644 index 0000000..19686b7 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALResizeBilinear.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_COMMON_H +#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_COMMON_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +// Offset function for positining corresponding index in input data +// int i0 - batches, int i1 - height, int i2 - width, int i3 - depth +inline int Offset(const luci_interpreter::RuntimeShape &shape, int i0, int i1, int i2, int i3) +{ + assert(shape.dimensionsCount() == 4); + + const int32_t *dims_data = reinterpret_cast(shape.dimsData()); + LUCI_INTERPRETER_CHECK(i0 >= 0 && i0 < dims_data[0]); + LUCI_INTERPRETER_CHECK(i1 >= 0 && i1 < dims_data[1]); + LUCI_INTERPRETER_CHECK(i2 >= 0 && i2 < dims_data[2]); + LUCI_INTERPRETER_CHECK(i3 >= 0 && i3 < dims_data[3]); + return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3; +} + +inline void ComputeInterpolationValues(const float value, const float scale, + const bool half_pixel_centers, int32_t input_size, + float *scaled_value, int32_t *lower_bound, + int32_t *upper_bound) +{ + if (half_pixel_centers) + { + *scaled_value = (value + 0.5f) * scale - 0.5f; + } + else + { + *scaled_value = value * scale; + } + float scaled_value_floor = std::floor(*scaled_value); + *lower_bound = std::max(static_cast(scaled_value_floor), static_cast(0)); + *upper_bound = std::min(static_cast(std::ceil(*scaled_value)), input_size - 1); +} + +template +static inline void +ResizeBilinear(const circle::ResizeBilinearOptions *op_params, + const luci_interpreter::RuntimeShape &unextended_input_shape, const T *input_data, + const luci_interpreter::RuntimeShape &unextended_output_size_shape, + const int32_t *output_size_data, + const luci_interpreter::RuntimeShape &unextended_output_shape, T *output_data) +{ + // If half_pixel_centers is True, align_corners must be False. + LUCI_INTERPRETER_CHECK(!op_params->half_pixel_centers() || !op_params->align_corners()); + + assert(unextended_input_shape.dimensionsCount() >= 4); + assert(unextended_output_size_shape.dimensionsCount() >= 1); + assert(unextended_output_shape.dimensionsCount() >= 4); + const luci_interpreter::RuntimeShape input_shape = + luci_interpreter::RuntimeShape::extendedShape(4, unextended_input_shape); + const luci_interpreter::RuntimeShape output_size_shape = + luci_interpreter::RuntimeShape::extendedShape(4, unextended_output_size_shape); + const luci_interpreter::RuntimeShape output_shape = + luci_interpreter::RuntimeShape::extendedShape(4, unextended_output_shape); + + int32_t batches = MatchingDim(input_shape, 0, output_shape, 0); + int32_t input_height = input_shape.dims(1); + int32_t input_width = input_shape.dims(2); + int32_t depth = MatchingDim(input_shape, 3, output_shape, 3); + + assert(output_size_shape.dims(0) == 1); + assert(output_size_shape.dims(1) == 1); + assert(output_size_shape.dims(2) == 1); + assert(output_size_shape.dims(3) == 2); + + int32_t output_height = output_size_data[Offset(output_size_shape, 0, 0, 0, 0)]; + int32_t output_width = output_size_data[Offset(output_size_shape, 0, 0, 0, 1)]; + + float height_scale = static_cast(input_height) / output_height; + float width_scale = static_cast(input_width) / output_width; + if (op_params->align_corners() && output_height > 1) + { + height_scale = static_cast(input_height - 1) / (output_height - 1); + } + if (op_params->align_corners() && output_width > 1) + { + width_scale = static_cast(input_width - 1) / (output_width - 1); + } + const float rounding_offset = std::numeric_limits::is_integer ? .5f : .0f; + + for (int b = 0; b < batches; ++b) + { + for (int y = 0; y < output_height; ++y) + { + float input_y; + int32_t y0, y1; + ComputeInterpolationValues(y, height_scale, op_params->half_pixel_centers(), input_height, + &input_y, &y0, &y1); + for (int x = 0; x < output_width; ++x) + { + float input_x; + int32_t x0, x1; + ComputeInterpolationValues(x, width_scale, op_params->half_pixel_centers(), input_width, + &input_x, &x0, &x1); + for (int c = 0; c < depth; ++c) + { + T interpolation = static_cast( + input_data[Offset(input_shape, b, y0, x0, c)] * (1 - (input_y - y0)) * + (1 - (input_x - x0)) + + input_data[Offset(input_shape, b, y1, x0, c)] * (input_y - y0) * (1 - (input_x - x0)) + + input_data[Offset(input_shape, b, y0, x1, c)] * (1 - (input_y - y0)) * (input_x - x0) + + input_data[Offset(input_shape, b, y1, x1, c)] * (input_y - y0) * (input_x - x0) + + rounding_offset); + output_data[Offset(output_shape, b, y, x, c)] = interpolation; + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALSoftmax.h b/onert-micro/luci-interpreter/pal/common/PALSoftmax.h new file mode 100644 index 0000000..a677856 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALSoftmax.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_COMMON_H +#define LUCI_INTERPRETER_PAL_SOFTMAX_COMMON_H + +namespace luci_interpreter_pal +{ +namespace +{ + +inline int flatSizeSkipDim(const luci_interpreter::RuntimeShape &shape, int skip_dim) +{ + const int dims_count = shape.dimensionsCount(); + const auto *dims_data = shape.dimsData(); + int flat_size = 1; + for (int i = 0; i < dims_count; ++i) + { + flat_size *= (i == skip_dim) ? 1 : dims_data[i]; + } + return flat_size; +} + +} // namespace + +inline void Softmax(const double beta, const luci_interpreter::RuntimeShape &input_shape, + const float *input_data, float *output_data) +{ + const int trailing_dim = input_shape.dimensionsCount() - 1; + const int outer_size = flatSizeSkipDim(input_shape, trailing_dim); + + const int depth = input_shape.dims(trailing_dim); + + for (int i = 0; i < outer_size; ++i) + { + // Find max element value which we'll use to ensure numerical stability + // taking advantage of the following equality: + // exp(x[i])/sum(exp(x[i])) == exp(x[i]+C)/sum(exp(x[i]+C)) + float max = std::numeric_limits::lowest(); + for (int c = 0; c < depth; ++c) + { + max = std::max(max, input_data[i * depth + c]); + } + + // Compute sum. + float sum = 0.f; + for (int c = 0; c < depth; ++c) + { + const float exp_c = std::exp((input_data[i * depth + c] - max) * static_cast(beta)); + output_data[i * depth + c] = exp_c; + sum += exp_c; + } + + // Compute result. + for (int c = 0; c < depth; ++c) + { + output_data[i * depth + c] = output_data[i * depth + c] / sum; + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SOFTMAX_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALStridedSlice.h b/onert-micro/luci-interpreter/pal/common/PALStridedSlice.h new file mode 100644 index 0000000..15b3209 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALStridedSlice.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_STRIDED_SLICE_H +#define LUCI_INTERPRETER_PAL_STRIDED_SLICE_H + +#include "Params.h" + +namespace luci_interpreter_pal +{ + +namespace +{ +// Use until std::clamp() is available from C++17. +inline int clamp(const int v, const int lo, const int hi) +{ + if (hi < v) + return hi; + if (v < lo) + return lo; + return v; +} + +inline bool loopCondition(int index, int stop, int stride) +{ + // True when we have reached the end of an axis and should loop. + return stride > 0 ? index >= stop : index <= stop; +} + +// Return the "real" index for the end of iteration along that axis. This is an +// "end" in the traditional C sense, in that it points to one past the last +// element. ie. So if you were iterating through all elements of a 1D array of +// size 4, this function would return 4 as the stop, because it is one past the +// "real" indices of 0, 1, 2 & 3. +inline int stopForAxis(const StridedSliceParams ¶ms, + const luci_interpreter::RuntimeShape &input_shape, int axis, + int start_for_axis) +{ + const auto end_mask = params.end_mask; + const auto shrink_axis_mask = params.shrink_axis_mask; + const auto *stop_indices = params.stop_indices; + const auto *strides = params.strides; + const int axis_size = input_shape.dims(axis); + if (axis_size == 0) + { + return 0; + } + + // Begin with the specified index + const bool shrink_axis = shrink_axis_mask & (1 << axis); + int stop = stop_indices[axis]; + + // When shrinking an axis, the end position does not matter (and can be + // incorrect when negative indexing is used, see Issue #19260). Always use + // start_for_axis + 1 to generate a length 1 slice, since start_for_axis has + // already been adjusted for negative indices. + if (shrink_axis) + { + return start_for_axis + 1; + } + + // end_mask override + if (end_mask & (1 << axis)) + { + if (strides[axis] > 0) + { + // Forward iteration - use the last element. These values will get + // clamped below + stop = std::numeric_limits::max(); + } + else + { + // Backward iteration - use the first element. + stop = std::numeric_limits::lowest(); + } + } + + // Handle negative indices + if (stop < 0) + { + stop += axis_size; + } + + // Clamping + // Because the end index points one past the last element, we need slightly + // different clamping ranges depending on the direction. + if (strides[axis] > 0) + { + // Forward iteration + stop = clamp(stop, 0, axis_size); + } + else + { + // Backward iteration + stop = clamp(stop, -1, axis_size - 1); + } + + return stop; +} + +// Return the index for the first element along that axis. This index will be a +// positive integer between [0, axis_size] (or [-1, axis_size -1] if stride < 0) +// that can be used to index directly into the data. +inline int startForAxis(const StridedSliceParams ¶ms, + const luci_interpreter::RuntimeShape &input_shape, int axis) +{ + const auto begin_mask = params.begin_mask; + const auto *start_indices = params.start_indices; + const auto *strides = params.strides; + const int axis_size = input_shape.dims(axis); + if (axis_size == 0) + { + return 0; + } + // Begin with the specified index. + int start = start_indices[axis]; + + // begin_mask override + if (begin_mask & 1 << axis) + { + if (strides[axis] > 0) + { + // Forward iteration - use the first element. These values will get + // clamped below (Note: We could have set them to 0 and axis_size-1, but + // use lowest() and max() to maintain symmetry with StopForAxis()) + start = std::numeric_limits::lowest(); + } + else + { + // Backward iteration - use the last element. + start = std::numeric_limits::max(); + } + } + + // Handle negative indices + if (start < 0) + { + start += axis_size; + } + + // Clamping + if (strides[axis] > 0) + { + // Forward iteration + start = clamp(start, 0, axis_size); + } + else + { + // Backward iteration + start = clamp(start, -1, axis_size - 1); + } + + return start; +} + +inline void stridedSlicePadIndices(StridedSliceParams *p, int dim_count) +{ + const int pad_count = dim_count - p->start_indices_count; + + // Pad indices at start, so move arrays by pad_count. + for (int i = p->start_indices_count - 1; i >= 0; --i) + { + p->strides[i + pad_count] = p->strides[i]; + p->start_indices[i + pad_count] = p->start_indices[i]; + p->stop_indices[i + pad_count] = p->stop_indices[i]; + } + for (int i = 0; i < pad_count; ++i) + { + p->start_indices[i] = 0; + p->stop_indices[i] = 1; + p->strides[i] = 1; + } + + // Pad masks with 0s or 1s as required. + p->shrink_axis_mask <<= pad_count; + p->ellipsis_mask <<= pad_count; + p->new_axis_mask <<= pad_count; + p->begin_mask <<= pad_count; + p->end_mask <<= pad_count; + p->begin_mask |= (1 << pad_count) - 1; + p->end_mask |= (1 << pad_count) - 1; + + p->start_indices_count = dim_count; + p->stop_indices_count = dim_count; + p->strides_count = dim_count; +} + +} // namespace + +template +inline void StridedSlice(StridedSliceParams &op_params, + const luci_interpreter::RuntimeShape &unextended_input_shape, + const T *input_data, T *output_data) +{ + const luci_interpreter::RuntimeShape input_shape = + luci_interpreter::RuntimeShape::extendedShape(5, unextended_input_shape); + + // Reverse and pad to 5 dimensions because that is what the runtime code + // requires (ie. all shapes must be 5D and are given backwards). + stridedSlicePadIndices(&op_params, 5); + + const int start_0 = startForAxis(op_params, input_shape, 0); + const int stop_0 = stopForAxis(op_params, input_shape, 0, start_0); + const int start_1 = startForAxis(op_params, input_shape, 1); + const int stop_1 = stopForAxis(op_params, input_shape, 1, start_1); + const int start_2 = startForAxis(op_params, input_shape, 2); + const int stop_2 = stopForAxis(op_params, input_shape, 2, start_2); + const int start_3 = startForAxis(op_params, input_shape, 3); + const int stop_3 = stopForAxis(op_params, input_shape, 3, start_3); + const int start_4 = startForAxis(op_params, input_shape, 4); + const int stop_4 = stopForAxis(op_params, input_shape, 4, start_4); + + for (int offset_0 = start_0 * input_shape.dims(1), end_0 = stop_0 * input_shape.dims(1), + step_0 = op_params.strides[0] * input_shape.dims(1); + !loopCondition(offset_0, end_0, op_params.strides[0]); offset_0 += step_0) + { + for (int offset_1 = (offset_0 + start_1) * input_shape.dims(2), + end_1 = (offset_0 + stop_1) * input_shape.dims(2), + step_1 = op_params.strides[1] * input_shape.dims(2); + !loopCondition(offset_1, end_1, op_params.strides[1]); offset_1 += step_1) + { + for (int offset_2 = (offset_1 + start_2) * input_shape.dims(3), + end_2 = (offset_1 + stop_2) * input_shape.dims(3), + step_2 = op_params.strides[2] * input_shape.dims(3); + !loopCondition(offset_2, end_2, op_params.strides[2]); offset_2 += step_2) + { + for (int offset_3 = (offset_2 + start_3) * input_shape.dims(4), + end_3 = (offset_2 + stop_3) * input_shape.dims(4), + step_3 = op_params.strides[3] * input_shape.dims(4); + !loopCondition(offset_3, end_3, op_params.strides[3]); offset_3 += step_3) + { + for (int offset_4 = offset_3 + start_4, end_4 = offset_3 + stop_4; + !loopCondition(offset_4, end_4, op_params.strides[4]); + offset_4 += op_params.strides[4]) + { + *output_data++ = input_data[offset_4]; + } + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_STRIDED_SLICE_H diff --git a/onert-micro/luci-interpreter/pal/common/PALSub.h b/onert-micro/luci-interpreter/pal/common/PALSub.h new file mode 100644 index 0000000..faa94fd --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALSub.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_SUB_COMMON_H +#define LUCI_INTERPRETER_PAL_SUB_COMMON_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ +template +static inline void Sub(const ArithmeticParams ¶ms, const int flat_size, const T *input1_data, + const T *input2_data, T *output_data) +{ + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + for (int i = 0; i < flat_size; ++i) + output_data[i] = + std::min(std::max(input1_data[i] - input2_data[i], activation_min), activation_max); +} + +template +inline void +BroadcastSub4DSlow(const ArithmeticParams ¶ms, + const luci_interpreter::RuntimeShape &input1_shape, const T *input1_data, + const luci_interpreter::RuntimeShape &input2_shape, const T *input2_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1, &desc2); + const luci_interpreter::RuntimeShape extended_output_shape = + luci_interpreter::RuntimeShape::extendedShape(4, output_shape); + + T activation_min, activation_max; + getActivationParams(params, &activation_min, &activation_max); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < extended_output_shape.dims(0); ++b) + { + for (int y = 0; y < extended_output_shape.dims(1); ++y) + { + for (int x = 0; x < extended_output_shape.dims(2); ++x) + { + for (int c = 0; c < extended_output_shape.dims(3); ++c) + { + const int output_data_offset = + ((b * extended_output_shape.dims(1) + y) * extended_output_shape.dims(2) + x) * + extended_output_shape.dims(3) + + c; + + output_data[output_data_offset] = + std::min(std::max(input1_data[subscriptToIndex(desc1, b, y, x, c)] - + input2_data[subscriptToIndex(desc2, b, y, x, c)], + activation_min), + activation_max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_SUB_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALTanh.h b/onert-micro/luci-interpreter/pal/common/PALTanh.h new file mode 100644 index 0000000..506657e --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALTanh.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_TANH_H +#define LUCI_INTERPRETER_PAL_TANH_H + +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +inline void Tanh(const int flat_size, const float *input_data, float *output_data) +{ + for (int i = 0; i < flat_size; i++) + { + float val = input_data[i]; + float result = std::tanh(val); + output_data[i] = result; + } +} + +inline void Tanh(int32_t input_multiplier, int32_t input_left_shift, const int flat_size, + const int16_t *ptr_input_data, int16_t *ptr_output_data) +{ + // We use the LUT for sigmoid and take into account, that + // tanh(x) = 2*sigmoid(2*x) - 1 + + // We scale by 3/4 to expand range [-8,8]->[-10.7,10.7]. + // In case of general parameter scale, multiplier 3 is taken into account + // in TanhPrepare function and it is included in + // input_multiplier already. + + if (input_multiplier == 0) + { // power of two case + input_multiplier = 3 << input_left_shift; + input_left_shift = 0; + } + + int32_t round = (input_left_shift > 0) ? 1 << (input_left_shift - 1) : 0; + + for (int i = 0; i < flat_size; ++i, ptr_input_data++, ptr_output_data++) + { + int32_t input_data = ((*ptr_input_data) * input_multiplier + round) >> input_left_shift; + + uint32_t abs_input_data = abs(input_data); + uint32_t uh = abs_input_data >> 8; + int32_t result; + + if (uh >= 255) + { + // Saturate to maximum. + result = 0xFFFF << 8; + } + else + { + uint32_t ua = sigmoid_table_uint16[uh]; + uint32_t ub = sigmoid_table_uint16[uh + 1]; + + uint8_t ut = abs_input_data & 0xFF; + + result = (ua << 8) + ut * (ub - ua); + } + + result = (input_data >= 0) ? (result - (1 << (14 + 9)) + (1 << (9 - 2))) + : (-result + (1 << (14 + 9)) + (1 << (9 - 2)) - 1); + + // Convert back to 16-bit. + result >>= (9 - 1); + + *ptr_output_data = result; + } +} + +#if 0 +inline void Tanh(int32_t input_zero_point, int32_t input_range_radius, + int32_t input_multiplier, int32_t input_shift, + const int flat_size, const int8_t* input_data, int8_t* output_data) { + // Integer bits must be in sync with Prepare() function. + static constexpr int32_t kInputIntegerBits = 4; + static constexpr int32_t kOutputScale = 7; + static constexpr int32_t kMinInt8 = std::numeric_limits::min(); + static constexpr int32_t kMaxInt8 = std::numeric_limits::max(); + + for (int i = 0; i < flat_size; ++i) { + const int32_t input = + static_cast(input_data[i]) - input_zero_point; + if (input <= -input_range_radius) { + output_data[i] = kMinInt8; + } else if (input >= input_range_radius) { + output_data[i] = kMaxInt8; + } else { + const int32_t input_in_q4 = + multiplyByQuantizedMultiplier(input, input_multiplier, input_shift); + const int32_t output_in_q0 = std::tanh(input_in_q4); + + int32_t output_in_q24 = + roundingDivideByPOT(output_in_q0, 31 - kOutputScale); + output_in_q24 = std::min(std::max(output_in_q24, kMinInt8), kMaxInt8); + output_data[i] = static_cast(output_in_q24); + } + } +} +#endif // 0 + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_TANH_H diff --git a/onert-micro/luci-interpreter/pal/common/PALTranspose.h b/onert-micro/luci-interpreter/pal/common/PALTranspose.h new file mode 100644 index 0000000..3381992 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALTranspose.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_TRANSPOSE_H +#define LUCI_INTERPRETER_PAL_TRANSPOSE_H + +#include "PALUtils.h" +#include "ProcessBroadcastShapes.h" + +namespace luci_interpreter_pal +{ +template +void TransposeImpl(const TransposeParams ¶ms, + const luci_interpreter::RuntimeShape &unextended_input_shape, + const T *input_data, + const luci_interpreter::RuntimeShape &unextended_output_shape, T *output_data) +{ + const int unextended_input_size = unextended_input_shape.dimensionsCount(); + const int unextended_output_size = unextended_output_shape.dimensionsCount(); + + const int input_ext_size = N - unextended_input_size; + const int output_ext_size = N - unextended_output_size; + NdArrayDesc input_desc; + NdArrayDesc output_desc; + copyDimsToDesc(luci_interpreter::RuntimeShape::extendedShape(N, unextended_input_shape), + &input_desc); + copyDimsToDesc(luci_interpreter::RuntimeShape::extendedShape(N, unextended_output_shape), + &output_desc); + + // The perm data is extended to match the output, each index incremented by + // the amount of front padding of the input shape. + int extended_perm[N]; + for (int i = 0; i < N; ++i) + { + extended_perm[i] = i < output_ext_size ? i : params.perm[i - output_ext_size] + input_ext_size; + } + + // Permutes the input shape so we don't need to permute the indexes inside + // the loop. Check to make sure output_dims is matching input_dims. + NdArrayDesc perm_input_desc; + for (int k = 0; k < N; ++k) + { + perm_input_desc.extents[k] = input_desc.extents[extended_perm[k]]; + perm_input_desc.strides[k] = input_desc.strides[extended_perm[k]]; + } + + // Naive transpose loop (iterate on output index and compute input index). + auto tranpose_func = [&](int indexes[N]) { + output_data[subscriptToIndex(output_desc, indexes)] = + input_data[subscriptToIndex(perm_input_desc, indexes)]; + }; + NDOpsHelper(output_desc, tranpose_func); +} + +template +void Transpose(const TransposeParams ¶ms, + const luci_interpreter::RuntimeShape &unextended_input_shape, const T *input_data, + const luci_interpreter::RuntimeShape &unextended_output_shape, T *output_data) +{ + // Transpose kernel only does rearranging values not numeric evaluations on + // each cell. It's safe to implement per size of scalar type and this trick + // keeps the total code size in a reasonable range. + switch (sizeof(T)) + { + case 1: + TransposeImpl(params, unextended_input_shape, + reinterpret_cast(input_data), + unextended_output_shape, reinterpret_cast(output_data)); + break; + case 2: + TransposeImpl(params, unextended_input_shape, + reinterpret_cast(input_data), + unextended_output_shape, reinterpret_cast(output_data)); + break; + + case 4: + TransposeImpl(params, unextended_input_shape, + reinterpret_cast(input_data), + unextended_output_shape, reinterpret_cast(output_data)); + break; + case 8: + TransposeImpl(params, unextended_input_shape, + reinterpret_cast(input_data), + unextended_output_shape, reinterpret_cast(output_data)); + break; + } +} +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_TRANSPOSE_H diff --git a/onert-micro/luci-interpreter/pal/common/PALUnidirectionalSequenceLSTMCommon.h b/onert-micro/luci-interpreter/pal/common/PALUnidirectionalSequenceLSTMCommon.h new file mode 100644 index 0000000..ad9631c --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALUnidirectionalSequenceLSTMCommon.h @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_UNIDIRECTIONAL_SEQUENCE_LSTM_COMMON_H +#define LUCI_INTERPRETER_PAL_UNIDIRECTIONAL_SEQUENCE_LSTM_COMMON_H + +#include "kernels/UnidirectionalSequenceLSTM.h" +#include "PALTanh.h" +#include "PALLogistic.h" +#include "PALFullyConnected.h" +#include "PALMul.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ +namespace lstm_internal +{ +namespace +{ +// Possible fused activation functions. +typedef enum +{ + kTfLiteActNone = 0, + kTfLiteActRelu, + kTfLiteActReluN1To1, // min(max(-1, x), 1) + kTfLiteActRelu6, // min(max(0, x), 6) + kTfLiteActTanh, + kTfLiteActSignBit, + kTfLiteActSigmoid, +} FusedActivation; + +} // namespace + +#ifndef DIS_QUANT + +template +void mulElementwise(int size, const ArithmeticParams *params, const InputType *input1_data, + const InputType *input2_data, OutputType *output_data) +{ + for (int i = 0; i < size; ++i) + { + const int32_t input1_val = params->input1_offset + input1_data[i]; + const int32_t input2_val = params->input2_offset + input2_data[i]; + const int32_t unclamped_result = + params->output_offset + multiplyByQuantizedMultiplier(input1_val * input2_val, + params->output_multiplier, + params->output_shift); + const int32_t clamped_output = + std::min(params->quantized_activation_max, + std::max(params->quantized_activation_min, unclamped_result)); + output_data[i] = static_cast(clamped_output); + } +} + +// Input and output have the same shape in LSTM +void mul(const luci_interpreter::RuntimeShape &shape, const ArithmeticParams *params, + const int16_t *input1_data, const int16_t *input2_data, int8_t *output_data) +{ + return mulElementwise(shape.flatSize(), params, input1_data, input2_data, + output_data); +} + +// Input and output have the same shape in LSTM +void mul(const luci_interpreter::RuntimeShape &shape, const ArithmeticParams *params, + const int16_t *input1_data, const int16_t *input2_data, int16_t *output_data) +{ + return mulElementwise(shape.flatSize(), params, input1_data, input2_data, output_data); +} + +void addElementWise(const int16_t *input_1, const int16_t *input_2, int n_batch, int n_input, + int16_t *output) +{ + for (int batch = 0; batch < n_batch; ++batch) + { + for (int i = 0; i < n_input; ++i) + { + const int index = batch * n_input + i; + int32_t sum = input_1[index] + input_2[index]; + const int32_t sum_clamped = + std::min(static_cast(std::numeric_limits::max()), + std::max(static_cast(std::numeric_limits::min()), sum)); + output[index] = static_cast(sum_clamped); + } + } +} + +void tanh(int32_t cell_state_scale_power, const luci_interpreter::RuntimeShape &input_data_shape, + int16_t *input_data, const luci_interpreter::RuntimeShape &output_data_shape, + int16_t *output_data) +{ + int32_t tanh_input_left_shift = (15 + cell_state_scale_power) - 3; + int32_t input_multiplier = 0; + if (tanh_input_left_shift < 0) /* handling negative shift value */ + { + tanh_input_left_shift = -tanh_input_left_shift; + input_multiplier = 3; + } + const int flat_size = input_data_shape.flatSize(); + luci_interpreter_pal::Tanh(input_multiplier, tanh_input_left_shift, flat_size, input_data, + output_data); +} + +void sigmoid(const luci_interpreter::RuntimeShape &data_shape, int16_t *data) +{ + luci_interpreter_pal::Logistic(0, 0, data_shape.flatSize(), data, data); +} + +void clipping(const int v_size, const luci_interpreter::lstm::CellStateInfo *cell_state_info, + int16_t *vector) +{ + for (int i = 0; i < v_size; i++) + { + vector[i] = std::max(std::min(cell_state_info->quantized_cell_clip, vector[i]), + static_cast(-cell_state_info->quantized_cell_clip)); + } +} +#endif // DIS_QUANT + +#ifndef DIS_FLOAT +// Input and output have the same shape in LSTM +void mul(const luci_interpreter::RuntimeShape &shape, const ArithmeticParams *params, + const float *input1_data, const float *input2_data, float *output_data) +{ + const int flat_size = shape.flatSize(); + return luci_interpreter_pal::Mul(*params, flat_size, input1_data, input2_data, output_data); +} + +void addElementWise(const float *input_1, const float *input_2, int n_batch, int n_input, + float *output) +{ + for (int batch = 0; batch < n_batch; ++batch) + { + for (int i = 0; i < n_input; ++i) + { + const int index = batch * n_input + i; + output[index] = input_1[index] + input_2[index]; + } + } +} + +void tanh(int32_t, const luci_interpreter::RuntimeShape &input_data_shape, float *input_data, + const luci_interpreter::RuntimeShape &output_data_shape, float *output_data) +{ + const int flat_size = input_data_shape.flatSize(); + luci_interpreter_pal::Tanh(flat_size, input_data, output_data); +} + +void sigmoid(const luci_interpreter::RuntimeShape &data_shape, float *data) +{ + const int flat_size = data_shape.flatSize(); + luci_interpreter_pal::Logistic(flat_size, data, data); +} + +void clipping(const int v_size, const luci_interpreter::lstm::CellStateInfo *cell_state_info, + float *vector) +{ + for (int i = 0; i < v_size; i++) + { + vector[i] = + std::max(std::min(cell_state_info->cell_clip, vector[i]), -cell_state_info->cell_clip); + } +} +#endif // DIS_FLOAT + +// Size information about the LSTM kernel, which is deduced from tensors stored +// in the flat buffer file. +struct LstmSizeInfo +{ + bool time_major; + int32_t batch_size; + int32_t time_steps; + int32_t input_dimension; + int32_t state_dimension; +}; + +class LstmStepManager +{ +public: + LstmStepManager() = delete; + // Does not take any ownership, and all pointers must refer to valid objects + // that outlive the one constructed. + explicit LstmStepManager(const LstmSizeInfo &size_info) : size_info_(size_info) {} + + void updateTime() + { + current_time_ += 1; + // default as one batch per inference + int input_step = size_info_.input_dimension; + int output_step = size_info_.state_dimension; + // time major: batch inference + if (size_info_.time_major) + { + input_step = input_step * size_info_.batch_size; + output_step = output_step * size_info_.batch_size; + } + + input_offset_ += input_step; + output_offset_ += output_step; + } + + void updateBatch() + { + current_batch_ += 1; + // batch inference for time major: no action needed + if (size_info_.time_major) + { + return; + } + // otherwise: singe batch inference, go to the next batch + hidden_state_offset_ += size_info_.state_dimension; + cell_state_offset_ += size_info_.state_dimension; + } + + void resetTime() { current_time_ = 0; } + + luci_interpreter::RuntimeShape inputShape() const + { + int batch_size = 1; + if (size_info_.time_major) + { + batch_size = size_info_.batch_size; + } + const int dims[2] = {batch_size, size_info_.input_dimension}; + const int32_t *dims_data = reinterpret_cast(dims); + return luci_interpreter::RuntimeShape(2, dims_data); + } + + luci_interpreter::RuntimeShape stateShape() const + { + int batch_size = 1; + if (size_info_.time_major) + { + batch_size = size_info_.batch_size; + } + const int dims[2] = {batch_size, size_info_.state_dimension}; + const int32_t *dims_data = reinterpret_cast(dims); + return luci_interpreter::RuntimeShape(2, dims_data); + } + + int inputOffset() const { return input_offset_; } + + int outputOffset() const { return output_offset_; } + + int hiddenStateOffset() const { return hidden_state_offset_; } + + int cellStateOffset() const { return cell_state_offset_; } + +private: + int32_t current_time_ = 0; + int32_t current_batch_ = 0; + int32_t input_offset_ = 0; + int32_t output_offset_ = 0; + int32_t hidden_state_offset_ = 0; + int32_t cell_state_offset_ = 0; + + const LstmSizeInfo &size_info_; +}; + +// Calculates a single LSTM gate. +// Implements the following formula: +// gate = activate(FC(input) + FC(recurrent)) +// Activation is sigmoid except for the "cell" gate (configurable, usually tanh) +template +void calculateLstmGate(const LstmStepManager *step_info, + const luci_interpreter::lstm::GateParameters *gate_params, + // Input FC + ActivationType *input_data, const circle::Tensor *input_weight, + const circle::Tensor *input_bias, + // Recurrent FC + ActivationType *recurrent_data, const circle::Tensor *recurrent_weight, + const circle::Tensor *recurrent_bias, + // Output + CellType *gate_output, + // Scratch arrays + CellType *fc_output_buffer, const FusedActivation activation, + luci_interpreter::BaseRuntimeGraph *runtime_graph) +{ + // Input FC + const auto gate_output_shape = step_info->stateShape(); + { + FullyConnectedParams op_params{}; + op_params.input_offset = gate_params->input_fc_params.input_offset; + op_params.weights_offset = gate_params->input_fc_params.weights_offset; + op_params.output_offset = gate_params->input_fc_params.output_offset; + op_params.output_multiplier = gate_params->input_fc_params.output_multiplier; + op_params.output_shift = gate_params->input_fc_params.output_shift; + op_params.quantized_activation_min = gate_params->input_fc_params.quantized_activation_min; + op_params.quantized_activation_max = gate_params->input_fc_params.quantized_activation_max; + op_params.float_activation_max = gate_params->input_fc_params.float_activation_max; + op_params.float_activation_min = gate_params->input_fc_params.float_activation_min; + + int32_t input_weight_shape[luci_interpreter::kMaxSmallSize]; + luci_interpreter::kernels::getTensorDims(input_weight, runtime_graph, input_weight_shape); + + FullyConnected(op_params, step_info->inputShape().dimsData(), + input_data + step_info->inputOffset(), input_weight_shape, + luci_interpreter::kernels::getTensorData( + runtime_graph->getConstDataByTensor(input_weight)), + luci_interpreter::kernels::getTensorData( + runtime_graph->getConstDataByTensor(input_bias)), + gate_output_shape.dimsData(), gate_output); + } + + // Recurrent FC + { + FullyConnectedParams op_params{}; + op_params.input_offset = gate_params->recurrent_fc_params.input_offset; + op_params.weights_offset = gate_params->recurrent_fc_params.weights_offset; + op_params.output_offset = gate_params->recurrent_fc_params.output_offset; + op_params.output_multiplier = gate_params->recurrent_fc_params.output_multiplier; + op_params.output_shift = gate_params->recurrent_fc_params.output_shift; + op_params.quantized_activation_min = gate_params->recurrent_fc_params.quantized_activation_min; + op_params.quantized_activation_max = gate_params->recurrent_fc_params.quantized_activation_max; + op_params.float_activation_max = gate_params->recurrent_fc_params.float_activation_max; + op_params.float_activation_min = gate_params->recurrent_fc_params.float_activation_min; + + int32_t recurrent_weight_shape[luci_interpreter::kMaxSmallSize]; + luci_interpreter::kernels::getTensorDims(recurrent_weight, runtime_graph, + recurrent_weight_shape); + + FullyConnected(op_params, step_info->stateShape().dimsData(), + recurrent_data + step_info->hiddenStateOffset(), recurrent_weight_shape, + luci_interpreter::kernels::getTensorData( + runtime_graph->getConstDataByTensor(recurrent_weight)), + luci_interpreter::kernels::getTensorData( + runtime_graph->getConstDataByTensor(recurrent_bias)), + gate_output_shape.dimsData(), fc_output_buffer); + + addElementWise(gate_output, fc_output_buffer, /*n_batch=*/gate_output_shape.dimsData()[0], + /*n_state=*/gate_output_shape.dimsData()[1], gate_output); + + switch (activation) + { + case FusedActivation::kTfLiteActSigmoid: + sigmoid(gate_output_shape, gate_output); + break; + case FusedActivation::kTfLiteActTanh: + { + // Set the scale power to -12 to avoid shift + tanh(/*cell_state_scale_power=*/-12, gate_output_shape, gate_output, gate_output_shape, + gate_output); + } + break; + default: + // Only Sigmoid or Tanh is used. + assert(false && "Only Sigmoid or Tanh is used"); + } + } +} + +// Update the hidden state of the LSTM kernel using the following formula: +// updated_hidden_state = Tanh(updated_cell_state) * output_gate_output, * means +// element wise multiplication +template +void updateLstmHidden(const LstmStepManager *step_info, CellType *cell_state_data_base, + ActivationType *hidden_state_data, const CellType *output_gate_output, + const ArithmeticParams *mul_params, int32_t cell_state_scale_power, + CellType *buffer) +{ + auto cell_state_shape = step_info->stateShape(); + CellType *cell_state_data = cell_state_data_base + step_info->cellStateOffset(); + // Tanh(cell_state) + tanh(cell_state_scale_power, cell_state_shape, cell_state_data, cell_state_shape, buffer); + // Update the hidden state + mul(cell_state_shape, mul_params, buffer, output_gate_output, + hidden_state_data + step_info->hiddenStateOffset()); +} + +// Update the cell state using the output from the forget gate, input gate, and +// cell gate Formula: updated_cell_state = forget_gate_output*cell_state + +// input_gate_output * cell_gate_output, where * denotes element wise +// multiplication +template +void updateLstmCell(const LstmStepManager *step_info, CellType *cell_state_data, + // Gate outputs + CellType *forget_gate_output, const CellType *input_gate_output, + const CellType *cell_gate_output, + // Mul parameters + const ArithmeticParams &forget_cell_mul_params, + const ArithmeticParams &input_mul_params, + const luci_interpreter::lstm::CellStateInfo *cell_state_info, CellType *buffer) +{ + auto cell_state_shape = step_info->stateShape(); + // Forget Gate x Cell State + mul(cell_state_shape, &forget_cell_mul_params, forget_gate_output, + cell_state_data + step_info->cellStateOffset(), + cell_state_data + step_info->cellStateOffset()); + // Input Gate x Cell Gate + mul(cell_state_shape, &input_mul_params, input_gate_output, cell_gate_output, buffer); + + // Update the cell state + addElementWise(cell_state_data + step_info->cellStateOffset(), buffer, + /*n_batch=*/cell_state_shape.dimsData()[0], + /*n_state=*/cell_state_shape.dimsData()[1], + cell_state_data + step_info->cellStateOffset()); + + if (cell_state_info->cell_clip > 0) + { + clipping(cell_state_shape.flatSize(), cell_state_info, + cell_state_data + step_info->cellStateOffset()); + } +} + +template +void lstmStep(luci_interpreter::lstm::LSTMStruct *lstm_struct, + luci_interpreter::lstm::LSTMParameters *lstm_params, LstmStepManager *step_info, + luci_interpreter::lstm::CellStateInfo *cell_state_info, + ActivationType *output_state_data, CellType *cell_state_data, CellType *scratch0, + CellType *scratch1, CellType *scratch2, CellType *scratch3, + luci_interpreter::BaseRuntimeGraph *runtime_graph) +{ + /*Step1: Calculate gate outputs to prepare cell state update*/ + CellType *gate_internal_buffer = scratch3; + CellType *forget_gate_output = scratch0; + + auto input_data = luci_interpreter::kernels::getTensorData( + runtime_graph->getDataByTensor(lstm_struct->input())); + + calculateLstmGate( + step_info, &lstm_params->forget_gate_parameters, + // Input FC + input_data, lstm_struct->input_to_forget_weights(), lstm_struct->forget_gate_bias(), + // Recurrent FC + output_state_data, lstm_struct->recurrent_to_forget_weights(), nullptr, + // Output + forget_gate_output, gate_internal_buffer, FusedActivation::kTfLiteActSigmoid, runtime_graph); + + // Input Gate calculation; + CellType *input_gate_output = scratch1; + calculateLstmGate( + step_info, &lstm_params->input_gate_parameters, + // Input FC + input_data, lstm_struct->input_to_input_weights(), lstm_struct->input_gate_bias(), + // Recurrent FC + output_state_data, lstm_struct->recurrent_to_input_weights(), + /*recurrent_bias*/ nullptr, + // Output + input_gate_output, + // Scratch arrays + gate_internal_buffer, FusedActivation::kTfLiteActSigmoid, runtime_graph); + + // Cell Gate calculation + CellType *cell_gate_output = scratch2; + calculateLstmGate( + step_info, &lstm_params->cell_gate_parameters, + // Input FC + input_data, lstm_struct->input_to_cell_weights(), lstm_struct->cell_gate_bias(), + // Recurrent FC + output_state_data, lstm_struct->recurrent_to_cell_weights(), + /*recurrent_bias*/ nullptr, + // Output + cell_gate_output, + // Scratch arrays + gate_internal_buffer, FusedActivation::kTfLiteActTanh, runtime_graph); + + /*Step2: update the cell state */ + { + // const InterGateParameters& inter_gate_params = op_data.inter_gate_parameters; + CellType *updated_input_buffer = scratch1; // reuse buffer + + updateLstmCell( + step_info, cell_state_data, forget_gate_output, input_gate_output, cell_gate_output, + lstm_params->inter_gate_parameters.forget_cell_mul_params, + lstm_params->inter_gate_parameters.input_mul_params, cell_state_info, updated_input_buffer); + } + + { + /*Step3: update the hidden state */ + CellType *output_gate_output = scratch1; // reuse buffer + calculateLstmGate( + step_info, &lstm_params->output_gate_parameters, + // Input FC + input_data, lstm_struct->input_to_output_weights(), lstm_struct->output_gate_bias(), + // Recurrent FC + output_state_data, lstm_struct->recurrent_to_output_weights(), nullptr, + // Output + output_gate_output, + // Scratch arrays + gate_internal_buffer, FusedActivation::kTfLiteActSigmoid, runtime_graph); + CellType *tanh_activated_cell_buffer = scratch0; // reuse buffer + updateLstmHidden( + step_info, cell_state_data, output_state_data, output_gate_output, + &lstm_params->inter_gate_parameters.output_mul_params, + cell_state_info->cell_state_scale_power, tanh_activated_cell_buffer); + + ActivationType *output_ptr = luci_interpreter::kernels::getTensorData( + runtime_graph->getDataByTensor(lstm_struct->output())); + std::memcpy(output_ptr + step_info->outputOffset(), + output_state_data + step_info->hiddenStateOffset(), + step_info->stateShape().flatSize() * sizeof(ActivationType)); + } +} + +} // namespace lstm_internal + +// Evaluate the LSTM kernel with (potential) multi-steps and multi-batch input +template +void evalLSTM(luci_interpreter::lstm::LSTMStruct *lstm_struct, + luci_interpreter::lstm::LSTMParameters *lstm_params, + luci_interpreter::lstm::CellStateInfo *cell_state_info, + ActivationType *output_state_data, CellType *cell_state_data, CellType *scratch0, + CellType *scratch1, CellType *scratch2, CellType *scratch3, + luci_interpreter::BaseRuntimeGraph *runtime_graph) +{ + lstm_internal::LstmSizeInfo size_info; + + size_info.time_major = lstm_struct->options->time_major(); + size_info.batch_size = size_info.time_major + ? luci_interpreter::Tensor::dim(lstm_struct->input(), 1) + : luci_interpreter::Tensor::dim(lstm_struct->input(), 0); + size_info.time_steps = size_info.time_major + ? luci_interpreter::Tensor::dim(lstm_struct->input(), 0) + : luci_interpreter::Tensor::dim(lstm_struct->input(), 1); + size_info.input_dimension = luci_interpreter::Tensor::dim(lstm_struct->input(), 2); + size_info.state_dimension = luci_interpreter::Tensor::dim(lstm_struct->output_state(), 1); + + lstm_internal::LstmStepManager step_info(size_info); + + // time is the first dimention, enable batch computation + if (size_info.time_major) + { + for (int t = 0; t < size_info.time_steps; t++) + { + lstm_internal::lstmStep( + lstm_struct, lstm_params, &step_info, cell_state_info, output_state_data, cell_state_data, + scratch0, scratch1, scratch2, scratch3, runtime_graph); + // prepare for the next time step + step_info.updateTime(); + } + } + else + { + // batch first, unable to size the input data. single batch inference + for (int b = 0; b < size_info.batch_size; b++) + { + for (int t = 0; t < size_info.time_steps; t++) + { + lstm_internal::lstmStep( + lstm_struct, lstm_params, &step_info, cell_state_info, output_state_data, cell_state_data, + scratch0, scratch1, scratch2, scratch3, runtime_graph); + // prepare for the next time step + step_info.updateTime(); + } + // prepare for the next batch + step_info.updateBatch(); + step_info.resetTime(); + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_UNIDIRECTIONAL_SEQUENCE_LSTM_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/common/PALUtils.h b/onert-micro/luci-interpreter/pal/common/PALUtils.h new file mode 100644 index 0000000..1e05bfc --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALUtils.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_UTILS_H +#define LUCI_INTERPRETER_PAL_UTILS_H + +#include + +namespace luci_interpreter_pal +{ + +// Table of sigmoid(i/24) at 0.16 format - 256 elements. +// We use combined sigmoid and tanh look-up table, since +// tanh(x) = 2*sigmoid(2*x) -1. +// Both functions are symmetric, so the LUT table is only needed +// for the absolute value of the input. +static const uint16_t sigmoid_table_uint16[256] = { + 32768, 33451, 34133, 34813, 35493, 36169, 36843, 37513, 38180, 38841, 39498, 40149, 40794, 41432, + 42064, 42688, 43304, 43912, 44511, 45102, 45683, 46255, 46817, 47369, 47911, 48443, 48964, 49475, + 49975, 50464, 50942, 51409, 51865, 52311, 52745, 53169, 53581, 53983, 54374, 54755, 55125, 55485, + 55834, 56174, 56503, 56823, 57133, 57433, 57724, 58007, 58280, 58544, 58800, 59048, 59288, 59519, + 59743, 59959, 60168, 60370, 60565, 60753, 60935, 61110, 61279, 61441, 61599, 61750, 61896, 62036, + 62172, 62302, 62428, 62549, 62666, 62778, 62886, 62990, 63090, 63186, 63279, 63368, 63454, 63536, + 63615, 63691, 63765, 63835, 63903, 63968, 64030, 64090, 64148, 64204, 64257, 64308, 64357, 64405, + 64450, 64494, 64536, 64576, 64614, 64652, 64687, 64721, 64754, 64786, 64816, 64845, 64873, 64900, + 64926, 64950, 64974, 64997, 65019, 65039, 65060, 65079, 65097, 65115, 65132, 65149, 65164, 65179, + 65194, 65208, 65221, 65234, 65246, 65258, 65269, 65280, 65291, 65301, 65310, 65319, 65328, 65337, + 65345, 65352, 65360, 65367, 65374, 65381, 65387, 65393, 65399, 65404, 65410, 65415, 65420, 65425, + 65429, 65433, 65438, 65442, 65445, 65449, 65453, 65456, 65459, 65462, 65465, 65468, 65471, 65474, + 65476, 65479, 65481, 65483, 65485, 65488, 65489, 65491, 65493, 65495, 65497, 65498, 65500, 65501, + 65503, 65504, 65505, 65507, 65508, 65509, 65510, 65511, 65512, 65513, 65514, 65515, 65516, 65517, + 65517, 65518, 65519, 65520, 65520, 65521, 65522, 65522, 65523, 65523, 65524, 65524, 65525, 65525, + 65526, 65526, 65526, 65527, 65527, 65528, 65528, 65528, 65529, 65529, 65529, 65529, 65530, 65530, + 65530, 65530, 65531, 65531, 65531, 65531, 65531, 65532, 65532, 65532, 65532, 65532, 65532, 65533, + 65533, 65533, 65533, 65533, 65533, 65533, 65533, 65534, 65534, 65534, 65534, 65534, 65534, 65534, + 65534, 65534, 65534, 65535}; + +inline std::int32_t saturatingRoundingDoublingHighMul(std::int32_t a, std::int32_t b) +{ + bool overflow = a == b && a == std::numeric_limits::min(); + std::int64_t a_64(a); + std::int64_t b_64(b); + std::int64_t ab_64 = a_64 * b_64; + std::int32_t nudge = ab_64 >= 0 ? (1 << 30) : (1 - (1 << 30)); + std::int32_t ab_x2_high32 = static_cast((ab_64 + nudge) / (1ll << 31)); + return overflow ? std::numeric_limits::max() : ab_x2_high32; +} + +// Correctly-rounded-to-nearest division by a power-of-two. +// Also known as a rounding arithmetic right shift. +inline int32_t roundingDivideByPOT(int32_t x, int32_t exponent) +{ + assert(exponent >= 0); + assert(exponent <= 31); + const int32_t mask = int32_t((1ll << exponent) - 1); + const int32_t zero = int32_t(0); + const int32_t one = int32_t(1); + const int32_t remainder = x & mask; + const int32_t threshold = (mask >> 1) + ((x < zero ? one : zero) & one); + return (x >> exponent) + ((remainder > threshold ? one : zero) & one); +} + +inline int32_t multiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier, int shift) +{ + int left_shift = shift > 0 ? shift : 0; + int right_shift = shift > 0 ? 0 : -shift; + return roundingDivideByPOT( + saturatingRoundingDoublingHighMul(x * (1 << left_shift), quantized_multiplier), right_shift); +} + +inline int32_t multiplyByQuantizedMultiplierSmallerThanOneExp(int32_t x, + int32_t quantized_multiplier, + int left_shift) +{ + return roundingDivideByPOT(saturatingRoundingDoublingHighMul(x, quantized_multiplier), + -left_shift); +} + +template inline void getActivationParams(const P ¶ms, int32_t *min, int32_t *max) +{ + *min = params.quantized_activation_min; + *max = params.quantized_activation_max; +} + +template inline void getActivationParams(const P ¶ms, float *min, float *max) +{ + *min = params.float_activation_min; + *max = params.float_activation_max; +} + +template inline void getActivationParams(const P ¶ms, int64_t *min, int64_t *max) +{ + *min = params.int64_activation_min; + *max = params.int64_activation_max; +} + +// Gets offset of index if reducing on axis. When reducing, the flattened offset +// will not change, if the input index changes on the given axis. For example, +// if you have a 3D tensor and you are reducing to 2D by eliminating axis 0, +// then index (0, 1, 2) and index (1, 1, 2) will map to the same flattened +// offset. +inline size_t reducedOutputOffset(const int num_dims, const int *dims, const int *index, + const int num_axis, const int *axis) +{ + if (num_dims == 0) + { + return 0; + } + size_t offset = 0; + for (int idx = 0; idx < num_dims; ++idx) + { + // if we need to skip this axis + bool is_axis = false; + if (axis != nullptr) + { + for (int axis_idx = 0; axis_idx < num_axis; ++axis_idx) + { + if (idx == axis[axis_idx]) + { + is_axis = true; + break; + } + } + } + if (!is_axis) + { + offset = offset * static_cast(dims[idx]) + static_cast(index[idx]); + } + } + return offset; +} + +// Gets next index to iterate through a multidimensional array. +inline bool nextIndex(const int num_dims, const int *dims, int *current) +{ + if (num_dims == 0) + { + return false; + } + int carry = 1; + for (int idx = num_dims - 1; idx >= 0; --idx) + { + int current_val = current[idx] + carry; + if (dims[idx] == current_val) + { + current[idx] = 0; + } + else + { + current[idx] = current_val; + carry = 0; + break; + } + } + return (carry == 0); +} + +// Get common shape dim, assert that they all agree. +inline int MatchingDim(const luci_interpreter::RuntimeShape &shape1, int index1, + const luci_interpreter::RuntimeShape &shape2, int index2) +{ + assert(shape1.dims(index1) == shape2.dims(index2)); + return shape1.dims(index1); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_UTILS_H diff --git a/onert-micro/luci-interpreter/pal/common/Params.h b/onert-micro/luci-interpreter/pal/common/Params.h new file mode 100644 index 0000000..0dea294 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/Params.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_PARAMS_H +#define LUCI_INTERPRETER_PAL_PARAMS_H + +namespace luci_interpreter_pal +{ + +struct PadParams +{ + int8_t left_padding_count; + int32_t left_padding[5]; + int8_t right_padding_count; + int32_t right_padding[5]; +}; + +struct FullyConnectedParams +{ + int32_t input_offset; + int32_t weights_offset; + int32_t output_offset; + int32_t output_multiplier; + int output_shift; + // uint8_t, etc, activation params. + int32_t quantized_activation_min; + int32_t quantized_activation_max; + // float activation params. + float float_activation_min; + float float_activation_max; + // Mark the operands as cacheable if they are unchanging, e.g. weights. + bool lhs_cacheable; + bool rhs_cacheable; +}; + +enum class PaddingType : uint8_t +{ + None, + Same, + Valid +}; + +struct PaddingValues +{ + int16_t width; + int16_t height; + // offset is used for calculating "remaining" padding, for example, `width` + // is 1 and `width_offset` is 1, so padding_left is 1 while padding_right is + // 1 + 1 = 2. + int16_t width_offset; + // Same as width_offset except it's over the height dimension. + int16_t height_offset; +}; + +struct ConvParams +{ + PaddingType padding_type; + PaddingValues padding_values; + int16_t stride_width; + int16_t stride_height; + int16_t dilation_width_factor; + int16_t dilation_height_factor; + // uint8_t inference params. + int32_t input_offset; + int32_t weights_offset; + int32_t output_offset; + int32_t output_multiplier; + int output_shift; + // uint8_t, etc, activation params. + int32_t quantized_activation_min; + int32_t quantized_activation_max; + // float activation params. + float float_activation_min; + float float_activation_max; +}; + +enum class BroadcastableOpCategory : uint8_t +{ + kNone, + kNonBroadcast, // Matching input shapes. + kFirstInputBroadcastsFast, // Fivefold nested loops. + kSecondInputBroadcastsFast, // Fivefold nested loops. + kGenericBroadcast, // Fall-back. + kScalarFirstBroadcast, // Scalar + kScalarSecondBroadcast, // Scalar +}; + +struct ConcatenationParams +{ + int8_t axis; + const int32_t *input_zeropoint; + const float *input_scale; + uint16_t inputs_count; + int32_t output_zeropoint; + float output_scale; +}; + +struct TransposeParams +{ + int8_t perm_count; + int32_t perm[5]; +}; + +struct ComparisonParams +{ + // uint8_t inference params. + int left_shift; + int32_t input1_offset; + int32_t input1_multiplier; + int input1_shift; + int32_t input2_offset; + int32_t input2_multiplier; + int input2_shift; + // Shape dependent / common to inference types. + bool is_broadcast; +}; + +struct StridedSliceParams +{ + int8_t start_indices_count; + int32_t start_indices[5]; + int8_t stop_indices_count; + int32_t stop_indices[5]; + int8_t strides_count; + int32_t strides[5]; + + int16_t begin_mask; + int16_t ellipsis_mask; + int16_t end_mask; + int16_t new_axis_mask; + int16_t shrink_axis_mask; +}; + +// For Add, Sub, Mul ops. +struct ArithmeticParams +{ + // Shape dependent / common to data / op types. + BroadcastableOpCategory broadcast_category; + // uint8_t inference params. + int32_t input1_offset; + int32_t input2_offset; + int32_t output_offset; + int32_t output_multiplier; + int output_shift; + // Add / Sub, not Mul, uint8_t inference params. + int left_shift; + int32_t input1_multiplier; + int input1_shift; + int32_t input2_multiplier; + int input2_shift; + // uint8_t, etc, activation params. + int32_t quantized_activation_min; + int32_t quantized_activation_max; + // float activation params. + float float_activation_min; + float float_activation_max; + // int64_t activation params. + int64_t int64_activation_min; + int64_t int64_activation_max; + + // Processed output dimensions. + // Let input "a" be the one that broadcasts in the faster-changing dimension. + // Then, after coalescing, for shapes {a0, a1, a2, a3, a4} and + // {b0, b1, b2, b3, b4}, + // broadcast_shape[4] = b0 = a0. + // broadcast_shape[3] = b1; a1 = 1. + // broadcast_shape[2] = b2 = a2. + // broadcast_shape[1] = a3; b3 = 1. + // broadcast_shape[0] = b4 = a4. + int broadcast_shape[5]; +}; + +enum class FusedActivationFunctionType : uint8_t +{ + kNone, + kRelu6, + kRelu1, + kRelu +}; + +struct PoolParams +{ + FusedActivationFunctionType activation; + PaddingType padding_type; + PaddingValues padding_values; + int stride_height; + int stride_width; + int filter_height; + int filter_width; + // uint8_t, etc, activation params. + int32_t quantized_activation_min; + int32_t quantized_activation_max; + // float activation params. + float float_activation_min; + float float_activation_max; +}; + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_PARAMS_H diff --git a/onert-micro/luci-interpreter/pal/common/ProcessBroadcastShapes.h b/onert-micro/luci-interpreter/pal/common/ProcessBroadcastShapes.h new file mode 100644 index 0000000..05ce802 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/ProcessBroadcastShapes.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_PROCESS_BROADCAST_SHAPES_H +#define LUCI_INTERPRETER_PAL_PROCESS_BROADCAST_SHAPES_H + +namespace luci_interpreter_pal +{ + +// DO NOT USE THIS STRUCT FOR NEW FUNCTIONALITY BEYOND IMPLEMENTING +// BROADCASTING. +// +// NdArrayDesc describes the shape and memory layout of an N-dimensional +// rectangular array of numbers. +// +// NdArrayDesc is basically identical to Dims defined in types.h. +// However, as Dims is to be deprecated, this class exists as an adaptor +// to enable simple unoptimized implementations of element-wise broadcasting +// operations. +template struct NdArrayDesc +{ + // The "extent" of each dimension. Indices along dimension d must be in the + // half-open interval [0, extents[d]). + int extents[N]; + + // The number of *elements* (not bytes) between consecutive indices of each + // dimension. + int strides[N]; +}; + +// Copies dims to desc, calculating strides. +template +inline void copyDimsToDesc(const luci_interpreter::RuntimeShape &input_shape, + NdArrayDesc *desc_out) +{ + int desc_stride = 1; + for (int i = N - 1; i >= 0; --i) + { + desc_out->extents[i] = input_shape.dims(i); + desc_out->strides[i] = desc_stride; + desc_stride *= input_shape.dims(i); + } +} + +template +typename std::enable_if::type NDOpsHelperImpl(const NdArrayDesc &output, + const Calc &calc, int indexes[N]) +{ + for (indexes[DIM] = 0; indexes[DIM] < output.extents[DIM]; ++indexes[DIM]) + { + calc(indexes); + } +} + +template +typename std::enable_if::type NDOpsHelperImpl(const NdArrayDesc &output, + const Calc &calc, int indexes[N]) +{ + for (indexes[DIM] = 0; indexes[DIM] < output.extents[DIM]; ++indexes[DIM]) + { + NDOpsHelperImpl(output, calc, indexes); + } +} + +// Execute the calc function in the innermost iteration based on the shape of +// the output. The calc function should take a single argument of type int[N]. +template +inline void NDOpsHelper(const NdArrayDesc &output, const Calc &calc) +{ + int indexes[N] = {0}; + NDOpsHelperImpl(output, calc, indexes); +} + +template +inline void NdArrayDescsForElementwiseBroadcast(const luci_interpreter::RuntimeShape &input0_shape, + const luci_interpreter::RuntimeShape &input1_shape, + NdArrayDesc *desc0_out, + NdArrayDesc *desc1_out) +{ + + auto extended_input0_shape = luci_interpreter::RuntimeShape::extendedShape(N, input0_shape); + auto extended_input1_shape = luci_interpreter::RuntimeShape::extendedShape(N, input1_shape); + + // Copy dims to desc, calculating strides. + copyDimsToDesc(extended_input0_shape, desc0_out); + copyDimsToDesc(extended_input1_shape, desc1_out); + + // Walk over each dimension. If the extents are equal do nothing. + // Otherwise, set the desc with extent 1 to have extent equal to the other and + // stride 0. + for (int i = 0; i < N; ++i) + { + const int extent0 = extended_input0_shape.dims(i); + const int extent1 = extended_input1_shape.dims(i); + if (extent0 != extent1) + { + if (extent0 == 1) + { + desc0_out->strides[i] = 0; + desc0_out->extents[i] = extent1; + } + else + { + desc1_out->strides[i] = 0; + desc1_out->extents[i] = extent0; + } + } + } +} + +inline int subscriptToIndex(const NdArrayDesc<4> &desc, int i0, int i1, int i2, int i3) +{ + return i0 * desc.strides[0] + i1 * desc.strides[1] + i2 * desc.strides[2] + i3 * desc.strides[3]; +} + +inline int subscriptToIndex(const NdArrayDesc<5> &desc, int indexes[5]) +{ + return indexes[0] * desc.strides[0] + indexes[1] * desc.strides[1] + + indexes[2] * desc.strides[2] + indexes[3] * desc.strides[3] + indexes[4] * desc.strides[4]; +} + +// Consolidates dimensions in broadcast inputs, checks for five-fold pattern. +// +// For example, if sequence of dimensions of one input is +// ..., 1, 3, 1, 7, 9, 5,... and the other is ..., 2, 3, 1, 7, 1, 1, ... +// we can consolidate these as +// ..., 1, 3*7, 9*5, ... and 2, 3*7, 1. +// +// The category is updated in the less-frequent case of shapes that are +// not suited to a fivefold-loop broadcast. +// +// Falls back to generic pattern when it does not know how to process properly. +// +// Returns true iff there is some sort of broadcast, which includes five-fold +// patterns and falling back to generic broadcast. +inline bool ProcessBroadcastShapes(const luci_interpreter::RuntimeShape &shape0, + const luci_interpreter::RuntimeShape &shape1, + luci_interpreter_pal::ArithmeticParams *params) +{ + const int dims_count = std::max(shape0.dimensionsCount(), shape1.dimensionsCount()); + + params->broadcast_category = BroadcastableOpCategory::kGenericBroadcast; + + auto extended_shape0 = luci_interpreter::RuntimeShape::extendedShape(dims_count, shape0); + auto extended_shape1 = luci_interpreter::RuntimeShape::extendedShape(dims_count, shape1); + + // Check for "exact" match, implicitly accepting any scalar shapes. + if (extended_shape0 == extended_shape1) + { + params->broadcast_category = BroadcastableOpCategory::kNonBroadcast; + return false; + } + + if (shape0.flatSize() == 1) + { + params->broadcast_category = BroadcastableOpCategory::kScalarFirstBroadcast; + return true; + } + else if (shape1.flatSize() == 1) + { + params->broadcast_category = BroadcastableOpCategory::kScalarSecondBroadcast; + return true; + } + + for (int i = dims_count - 1; i >= 0; --i) + { + if (extended_shape0.dims(i) == extended_shape1.dims(i)) + { + continue; + } + else if (extended_shape0.dims(i) == 1) + { + params->broadcast_category = BroadcastableOpCategory::kFirstInputBroadcastsFast; + return true; + } + else if (extended_shape1.dims(i) == 1) + { + params->broadcast_category = BroadcastableOpCategory::kSecondInputBroadcastsFast; + return true; + } + else + { + // This case is erroneous: there is a dimension that does not match and + // is not a broadcast from one shape to the other. + params->broadcast_category = BroadcastableOpCategory::kGenericBroadcast; + return true; + } + } + + return false; +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_PROCESS_BROADCAST_SHAPES_H diff --git a/onert-micro/luci-interpreter/pal/linux/KernelsToBuild.lst b/onert-micro/luci-interpreter/pal/linux/KernelsToBuild.lst deleted file mode 100644 index 1f4ccb3..0000000 --- a/onert-micro/luci-interpreter/pal/linux/KernelsToBuild.lst +++ /dev/null @@ -1,77 +0,0 @@ -REGISTER_KERNEL(ADD, Add) -REGISTER_KERNEL(ARG_MAX, ArgMax) -REGISTER_KERNEL(AVERAGE_POOL_2D, AveragePool2D) -REGISTER_KERNEL(BATCH_MATMUL, BatchMatMul) -REGISTER_KERNEL(BATCH_TO_SPACE_ND, BatchToSpaceND) -REGISTER_KERNEL(CAST, Cast) -REGISTER_KERNEL(CONCATENATION, Concatenation) -REGISTER_KERNEL(CONV_2D, Conv2D) -REGISTER_KERNEL(DEPTH_TO_SPACE, DepthToSpace) -REGISTER_KERNEL(DEPTHWISE_CONV_2D, DepthwiseConv2D) -REGISTER_KERNEL(DEQUANTIZE, Dequantize) -REGISTER_KERNEL(DIV, Div) -REGISTER_KERNEL(ELU, Elu) -REGISTER_KERNEL(EXP, Exp) -REGISTER_KERNEL(EXPAND_DIMS, ExpandDims) -REGISTER_KERNEL(FILL, Fill) -REGISTER_KERNEL(FLOOR, Floor) -REGISTER_KERNEL(FLOOR_DIV, FloorDiv) -REGISTER_KERNEL(EQUAL, Equal) -REGISTER_KERNEL(FULLY_CONNECTED, FullyConnected) -REGISTER_KERNEL(GATHER, Gather) -REGISTER_KERNEL(GREATER, Greater) -REGISTER_KERNEL(GREATER_EQUAL, GreaterEqual) -REGISTER_KERNEL(IF, If) -REGISTER_KERNEL(INSTANCE_NORM, InstanceNorm) -REGISTER_KERNEL(L2_NORMALIZATION, L2Normalize) -REGISTER_KERNEL(L2_POOL_2D, L2Pool2D) -REGISTER_KERNEL(LEAKY_RELU, LeakyRelu) -REGISTER_KERNEL(LESS, Less) -REGISTER_KERNEL(LESS_EQUAL, LessEqual) -REGISTER_KERNEL(LOCAL_RESPONSE_NORMALIZATION, LocalResponseNormalization) -REGISTER_KERNEL(LOGICAL_AND, LogicalAnd) -REGISTER_KERNEL(LOGICAL_NOT, LogicalNot) -REGISTER_KERNEL(LOGICAL_OR, LogicalOr) -REGISTER_KERNEL(LOGISTIC, Logistic) -REGISTER_KERNEL(LOG_SOFTMAX, LogSoftmax) -REGISTER_KERNEL(MAXIMUM, Maximum) -REGISTER_KERNEL(MAX_POOL_2D, MaxPool2D) -REGISTER_KERNEL(MEAN, Mean) -REGISTER_KERNEL(MINIMUM, Minimum) -REGISTER_KERNEL(MIRROR_PAD, MirrorPad) -REGISTER_KERNEL(MUL, Mul) -REGISTER_KERNEL(NEG, Neg) -REGISTER_KERNEL(NOT_EQUAL, NotEqual) -REGISTER_KERNEL(ONE_HOT, OneHot) -REGISTER_KERNEL(PACK, Pack) -REGISTER_KERNEL(PAD, Pad) -REGISTER_KERNEL(PADV2, PadV2) -REGISTER_KERNEL(POW, Pow) -REGISTER_KERNEL(PRELU, PRelu) -REGISTER_KERNEL(QUANTIZE, Quantize) -REGISTER_KERNEL(RELU, Relu) -REGISTER_KERNEL(RELU6, Relu6) -REGISTER_KERNEL(RESHAPE, Reshape) -REGISTER_KERNEL(RESIZE_BILINEAR, ResizeBilinear) -REGISTER_KERNEL(RESIZE_NEAREST_NEIGHBOR, ResizeNearestNeighbor) -REGISTER_KERNEL(REVERSE_V2, ReverseV2) -REGISTER_KERNEL(RSQRT, Rsqrt) -REGISTER_KERNEL(SHAPE, Shape) -REGISTER_KERNEL(SLICE, Slice) -REGISTER_KERNEL(SOFTMAX, Softmax) -REGISTER_KERNEL(SPACE_TO_BATCH_ND, SpaceToBatchND) -REGISTER_KERNEL(SPACE_TO_DEPTH, SpaceToDepth) -REGISTER_KERNEL(SPLIT, Split) -REGISTER_KERNEL(SPLIT_V, SplitV) -REGISTER_KERNEL(STRIDED_SLICE, StridedSlice) -REGISTER_KERNEL(SQRT, Sqrt) -REGISTER_KERNEL(SQUARE, Square) -REGISTER_KERNEL(SQUARED_DIFFERENCE, SquaredDifference) -REGISTER_KERNEL(SQUEEZE, Squeeze) -REGISTER_KERNEL(SUB, Sub) -REGISTER_KERNEL(SVDF, SVDF) -REGISTER_KERNEL(TANH, Tanh) -REGISTER_KERNEL(TRANSPOSE, Transpose) -REGISTER_KERNEL(TRANSPOSE_CONV, TransposeConv) -REGISTER_KERNEL(UNPACK, Unpack) -REGISTER_KERNEL(WHILE, While) diff --git a/onert-micro/luci-interpreter/pal/linux/PALArgMax.h b/onert-micro/luci-interpreter/pal/linux/PALArgMax.h deleted file mode 100644 index 84147b2..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALArgMax.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H -#define LUCI_INTERPRETER_PAL_ARGMAX_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, - const T2 *axis, const tflite::RuntimeShape &output_shape, - T3 *output_data, const std::greater cmp) -{ - tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALAveragePool2d.h b/onert-micro/luci-interpreter/pal/linux/PALAveragePool2d.h deleted file mode 100644 index fe93b67..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALAveragePool2d.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H -#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H - -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data, - const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) -{ - { - // MARK: At this moment this operation doesn't support - assert(false && "AveragePool NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, - int8_t *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - - tflite::reference_integer_ops::AveragePool(params, input_shape, input_data, output_shape, - output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &output_shape) - -{ - (void)input_data_type; - (void)input_shape; - (void)output_shape; - - scratchpad->set_allocatable(false); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALBatchMatMul.h b/onert-micro/luci-interpreter/pal/linux/PALBatchMatMul.h deleted file mode 100644 index a6f6564..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALBatchMatMul.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_BATCHMATMUL_H -#define LUCI_INTERPRETER_PAL_BATCHMATMUL_H - -#include - -namespace luci_interpreter_pal -{ -inline void BatchMatMul(const tflite::RuntimeShape &lhs_shape, const float *lhs_data, - const tflite::RuntimeShape &rhs_shape, const float *rhs_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::BatchMatMul(lhs_shape, lhs_data, rhs_shape, rhs_data, output_shape, - output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *lhs_scratchpad, - luci_interpreter::Tensor *rhs_scratchpad, - const tflite::RuntimeShape &lhs_shape, - const tflite::RuntimeShape &rhs_shape) -{ - // Scratchpad for transposed LHS - { - auto lhs_rank = lhs_shape.DimensionsCount(); - luci_interpreter::Shape scratchpad_size(lhs_rank); - for (int i = 0; i < lhs_rank - 2; ++i) - { - scratchpad_size.dim(i) = lhs_shape.Dims(i); - } - scratchpad_size.dim(lhs_rank - 2) = lhs_shape.Dims(lhs_rank - 1); - scratchpad_size.dim(lhs_rank - 1) = lhs_shape.Dims(lhs_rank - 2); - - lhs_scratchpad->resize(scratchpad_size); - } - // Scratchpad for transposed RHS - { - auto rhs_rank = rhs_shape.DimensionsCount(); - luci_interpreter::Shape scratchpad_size(rhs_rank); - for (int i = 0; i < rhs_rank - 2; ++i) - { - scratchpad_size.dim(i) = rhs_shape.Dims(i); - } - scratchpad_size.dim(rhs_rank - 2) = rhs_shape.Dims(rhs_rank - 1); - scratchpad_size.dim(rhs_rank - 1) = rhs_shape.Dims(rhs_rank - 2); - - rhs_scratchpad->resize(scratchpad_size); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_BATCHMATMUL_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h b/onert-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h deleted file mode 100644 index 52da2a8..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALBatchToSpaceND.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H -#define LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32 *crops_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::BatchToSpaceND( - unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, crops_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALConv2d.h b/onert-micro/luci-interpreter/pal/linux/PALConv2d.h deleted file mode 100644 index 1c26586..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALConv2d.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_CONV2D_H -#define LUCI_INTERPRETER_PAL_CONV2D_H - -#include -#include - -namespace luci_interpreter_pal -{ -static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, - const float *input_data, const tflite::RuntimeShape &filter_shape, - const float *filter_data, const tflite::RuntimeShape &bias_shape, - const float *bias_data, const tflite::RuntimeShape &output_shape, - float *output_data, const tflite::RuntimeShape &scratchpad_shape, - float *scratchpad_data) -{ - (void)scratchpad_shape; - if (scratchpad_data) - { - const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); - const int32_t input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); - const int32_t output_height = output_shape.Dims(1); - const int32_t output_width = output_shape.Dims(2); - const int32_t filter_height = filter_shape.Dims(1); - const int32_t filter_width = filter_shape.Dims(2); - tflite::RuntimeShape im2col_shape{batches, output_height, output_width, - input_depth * filter_height * filter_width}; - - tflite::optimized_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data, im2col_shape, - scratchpad_data); - } - else - tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data, - tflite::RuntimeShape(), nullptr); -} - -static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, - const uint8 *input_data, const tflite::RuntimeShape &filter_shape, - const uint8 *filter_data, const tflite::RuntimeShape &bias_shape, - const int32 *bias_data, const tflite::RuntimeShape &output_shape, - uint8 *output_data, const tflite::RuntimeShape &scratchpad_shape, - uint8 *scratchpad_data) -{ - // TODO This should only be done once (although it takes only a few microseconds). - // Also, the user should be able to adjust the number of threads. - auto gemmlowp_context = std::make_unique(); - gemmlowp_context->set_max_num_threads(static_cast(std::thread::hardware_concurrency())); - - tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data, scratchpad_shape, - scratchpad_data, gemmlowp_context.get()); -} - -static inline void ConvPerChannel(const tflite::ConvParams ¶ms, const int32_t *mult, - const int32_t *shifts, const tflite::RuntimeShape &input_shape, - const int8 *input_data, const tflite::RuntimeShape &filter_shape, - const int8 *filter_data, const tflite::RuntimeShape &bias_shape, - const int32 *bias_data, const tflite::RuntimeShape &output_shape, - int8 *output_data, const tflite::RuntimeShape &scratchpad_shape, - int8 *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - // TODO enable optimized version - tflite::reference_integer_ops::ConvPerChannel(params, mult, shifts, input_shape, input_data, - filter_shape, filter_data, bias_shape, bias_data, - output_shape, output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const luci_interpreter::DataType &input_data_type, - const tflite::ConvParams ¶ms, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &filter_shape, - const tflite::RuntimeShape &output_shape) -{ - const int32_t filter_height = filter_shape.Dims(1); - const int32_t filter_width = filter_shape.Dims(2); - - // Allocate tensor for scratchpad, if needed. - // The checks here should be aligned with the actual implementation. - const bool need_dilated_scratchpad = - params.dilation_height_factor != 1 || params.dilation_width_factor != 1; - const bool need_non_dilated_scratchpad = params.stride_height != 1 || params.stride_width != 1 || - filter_height != 1 || filter_width != 1; - auto _need_scratchpad = input_data_type != luci_interpreter::DataType::S16 && - (need_dilated_scratchpad || need_non_dilated_scratchpad); - - if (_need_scratchpad) - { - const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0); - const int32_t input_depth = tflite::MatchingDim(input_shape, 3, filter_shape, 3); - const int32_t output_height = output_shape.Dims(1); - const int32_t output_width = output_shape.Dims(2); - - auto data_type_size = static_cast(luci_interpreter::getDataTypeSize(input_data_type)); - int32_t scratchpad_size = batches * output_width * output_height * input_depth * filter_height * - filter_width * data_type_size; - luci_interpreter::Shape scratchpad_shape{scratchpad_size}; - scratchpad->resize(scratchpad_shape); - } - else - { - scratchpad->set_allocatable(false); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_CONV2D_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALDepthToSpace.h b/onert-micro/luci-interpreter/pal/linux/PALDepthToSpace.h deleted file mode 100644 index 6372e4e..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALDepthToSpace.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H -#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::DepthToSpace(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h b/onert-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h deleted file mode 100644 index 1cba996..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALDepthwiseConv2d.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H -#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H - -#include -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void -DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &filter_shape, - const T *filter_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, const tflite::RuntimeShape &output_shape, - T *output_data, const tflite::RuntimeShape &scratchpad_shape, - T *scratchpad_data) -{ - { - // MARK: At this moment this operation is not supported - assert(false && "DepthwiseConvPerChannel NYI"); - (void)params; - (void)output_multiplier; - (void)output_shift; - (void)input_shape; - (void)output_data; - (void)input_data; - (void)filter_shape; - (void)filter_data; - (void)bias_shape; - (void)bias_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void DepthwiseConvPerChannel( - const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - tflite::reference_integer_ops::DepthwiseConvPerChannel( - params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const tflite::DepthwiseParams ¶ms, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &filter_shape, - const tflite::RuntimeShape &output_shape) - -{ - (void)params; - (void)input_data_type; - (void)input_shape; - (void)filter_shape; - (void)output_shape; - - scratchpad->set_allocatable(false); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALDequantize.h b/onert-micro/luci-interpreter/pal/linux/PALDequantize.h deleted file mode 100644 index b505129..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALDequantize.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H -#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Dequantize(tflite::DequantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::optimized_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALFullyConnected.h b/onert-micro/luci-interpreter/pal/linux/PALFullyConnected.h deleted file mode 100644 index ed8d2a6..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALFullyConnected.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_FULLYCONNECTED_H -#define LUCI_INTERPRETER_PAL_FULLYCONNECTED_H - -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void FullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &filter_shape, const T *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - { - // MARK: At this moment this operation doesn't support - assert(false && "FullyConnected NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)filter_shape; - (void)filter_data; - (void)bias_shape; - (void)bias_data; - (void)output_shape; - (void)output_data; - } -} - -template <> -inline void -FullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data) -{ - tflite::reference_integer_ops::FullyConnected(params, input_shape, input_data, filter_shape, - filter_data, bias_shape, bias_data, output_shape, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_FULLYCONNECTED_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALGather.h b/onert-micro/luci-interpreter/pal/linux/PALGather.h deleted file mode 100644 index 97d5431..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALGather.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_GATHER_H -#define LUCI_INTERPRETER_PAL_GATHER_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Gather(const tflite::GatherParams &op_params, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &coords_shape, const CoordsT *coords_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::Gather(op_params, input_shape, input_data, coords_shape, coords_data, - output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_GATHER_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALL2Normalize.h b/onert-micro/luci-interpreter/pal/linux/PALL2Normalize.h deleted file mode 100644 index 5d2c09f..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALL2Normalize.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H -#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::L2Normalization(op_params, input_shape, input_data, output_shape, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALL2Pool2D.h b/onert-micro/luci-interpreter/pal/linux/PALL2Pool2D.h deleted file mode 100644 index 121ced9..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALL2Pool2D.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_L2POOL2D_H -#define LUCI_INTERPRETER_PAL_L2POOL2D_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void L2Pool(const tflite::PoolParams ¶ms, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &output_shape, - T *output_data) -{ - tflite::optimized_ops::L2Pool(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_L2POOL2D_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h b/onert-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h deleted file mode 100644 index b996650..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALLocalResponseNormalization.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H -#define LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H - -#include - -namespace luci_interpreter_pal -{ -static inline void -LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::optimized_ops::LocalResponseNormalization(op_params, input_shape, input_data, - output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_LOCALRESPONSENORMALIZATION_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALLogSoftmax.h b/onert-micro/luci-interpreter/pal/linux/PALLogSoftmax.h deleted file mode 100644 index 099b2d2..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALLogSoftmax.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_LOGSOFTMAX_H -#define LUCI_INTERPRETER_PAL_LOGSOFTMAX_H - -#include - -namespace luci_interpreter_pal -{ -static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, - float beta) -{ - tflite::optimized_ops::PopulateSoftmaxLookupTable(data, input_scale, beta); -} - -static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) -{ - // Do nothing for linux - (void)params; - (void)input_scale; - (void)beta; -} - -static inline void LogSoftmax(const tflite::SoftmaxParams ¶ms, float input_scale, - const tflite::RuntimeShape &input_shape, const uint8 *input_data, - const tflite::RuntimeShape &output_shape, uint8 *output_data) -{ - tflite::optimized_ops::LogSoftmax(params, input_scale, input_shape, input_data, output_shape, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_LOGSOFTMAX_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALMul.h b/onert-micro/luci-interpreter/pal/linux/PALMul.h deleted file mode 100644 index d2a4d1f..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALMul.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_MUL_H -#define LUCI_INTERPRETER_PAL_MUL_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, - const T *input1_data, const tflite::RuntimeShape &input2_shape, - const T *input2_data, const tflite::RuntimeShape &output_shape, - T *output_data) -{ - tflite::optimized_ops::Mul(params, input1_shape, input1_data, input2_shape, input2_data, - output_shape, output_data); -} - -template <> -inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, - const int64_t *input1_data, const tflite::RuntimeShape &input2_shape, - const int64_t *input2_data, const tflite::RuntimeShape &output_shape, - int64_t *output_data) -{ - tflite::optimized_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, - input2_data, output_shape, output_data); -} - -template -static inline void -BroadcastMul4DSlow(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, - const T *input1_data, const tflite::RuntimeShape &input2_shape, - const T *input2_data, const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, - input2_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALQuantize.h b/onert-micro/luci-interpreter/pal/linux/PALQuantize.h deleted file mode 100644 index ebaf876..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALQuantize.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H -#define LUCI_INTERPRETER_PAL_QUANTIZE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Quantize(tflite::QuantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); -} - -template -static inline void Requantize(const Input *input_data, int32_t size, - int32_t effective_scale_multiplier, int32_t effective_scale_shift, - int32_t input_zero_point, int32_t output_zero_point, - Output *output_data) -{ - tflite::optimized_ops::Requantize(input_data, size, effective_scale_multiplier, - effective_scale_shift, input_zero_point, output_zero_point, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALRelu.h b/onert-micro/luci-interpreter/pal/linux/PALRelu.h deleted file mode 100644 index de17750..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALRelu.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RELU_H -#define LUCI_INTERPRETER_PAL_RELU_H - -#include - -namespace luci_interpreter_pal -{ -static inline void Relu(const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::optimized_ops::Relu(input_shape, input_data, output_shape, output_data); -} - -template -static inline void ReluX(const tflite::ReluParams ¶ms, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &output_shape, - T *output_data) -{ - tflite::optimized_ops::ReluX(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RELU_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALRelu6.h b/onert-micro/luci-interpreter/pal/linux/PALRelu6.h deleted file mode 100644 index e546a2c..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALRelu6.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RELU6_H -#define LUCI_INTERPRETER_PAL_RELU6_H - -#include - -namespace luci_interpreter_pal -{ -static inline void Relu6(const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::optimized_ops::Relu6(input_shape, input_data, output_shape, output_data); -} - -template -static inline void ReluX(const tflite::ReluParams ¶ms, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &output_shape, - T *output_data) -{ - tflite::optimized_ops::ReluX(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RELU6_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALResizeBilinear.h b/onert-micro/luci-interpreter/pal/linux/PALResizeBilinear.h deleted file mode 100644 index 0ccfa04..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALResizeBilinear.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H -#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeBilinear(const tflite::ResizeBilinearParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h b/onert-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h deleted file mode 100644 index c500611..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALResizeNearestNeighbor.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H -#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, const int32 *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSVDF.h b/onert-micro/luci-interpreter/pal/linux/PALSVDF.h deleted file mode 100644 index 438c63d..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSVDF.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SVDF_H -#define LUCI_INTERPRETER_PAL_SVDF_H - -#include - -namespace luci_interpreter_pal -{ -static inline void -IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, - const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, int16_t *activation_state_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, - int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, - int scale_2_b, int32_t input_zp, int32_t output_zp) -{ - tflite::reference_ops::EvalIntegerSVDF(¶ms, input_shape, input_data, weight_feature_shape, - weight_feature_data, weight_time_shape, weight_time_data, - bias_shape, bias_data, activation_state_data, output_shape, - output_data, scratchpad_data, output_temp_data, scale_1_a, - scale_1_b, scale_2_a, scale_2_b, input_zp, output_zp); -} -static inline void -FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const float *input_data, const tflite::RuntimeShape &weight_feature_shape, - const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const float *weight_time_data, const tflite::RuntimeShape &bias_shape, - const float *bias_data, float *scratchpad_data, float *activation_state_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::EvalFloatSVDF(¶ms, input_shape, input_data, weight_feature_shape, - weight_feature_data, weight_time_shape, weight_time_data, - bias_shape, bias_data, scratchpad_data, - activation_state_data, output_shape, output_data); -} - -static inline void SetupScratchpadTensor( - const luci_interpreter::DataType &input_data_type, - const luci_interpreter::DataType &weight_feature_data_type, - luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, - luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, - luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, - const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, - const int32_t batch_size, const int32_t num_filters, const int32_t num_units) -{ - - if (input_data_type == luci_interpreter::DataType::FLOAT32 && - (weight_feature_data_type == luci_interpreter::DataType::S8 || - weight_feature_data_type == luci_interpreter::DataType::U8)) - { - (void)input_shape; - (void)weight_time_shape; - (void)scratchpad_3; - (void)scratchpad_4; - (void)scratchpad_5; - (void)scratchpad_6; - - assert(false && "Hybrid type is not currently supported for linux platform"); - } - - // Resize scratchpad_1 tensor - scratchpad_1->resize({batch_size, num_filters}); - - if (input_data_type == luci_interpreter::DataType::S8) - { - // Resize scratchpad_2 for full_integer op - scratchpad_2->resize({batch_size, num_units}); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSlice.h b/onert-micro/luci-interpreter/pal/linux/PALSlice.h deleted file mode 100644 index 0984bf7..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSlice.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SLICE_H -#define LUCI_INTERPRETER_PAL_SLICE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Slice(const tflite::SliceParams &op_params, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::Slice(op_params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SLICE_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSoftmax.h b/onert-micro/luci-interpreter/pal/linux/PALSoftmax.h deleted file mode 100644 index abd38a5..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSoftmax.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H -#define LUCI_INTERPRETER_PAL_SOFTMAX_H - -#include - -namespace luci_interpreter_pal -{ -static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, - float beta) -{ - tflite::optimized_ops::PopulateSoftmaxLookupTable(data, input_scale, beta); -} - -static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) -{ - // Do nothing for linux - (void)params; - (void)input_scale; - (void)beta; -} - -template -static inline void Softmax(const tflite::SoftmaxParams ¶ms, - const tflite::RuntimeShape &input_shape, const In *input_data, - const tflite::RuntimeShape &output_shape, Out *output_data) -{ - tflite::optimized_ops::Softmax(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h b/onert-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h deleted file mode 100644 index 7275799..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSpaceToBatchND.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H -#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, - const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32 *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32 *paddings_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::SpaceToBatchND( - params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, paddings_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h b/onert-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h deleted file mode 100644 index 3013fc1..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSpaceToDepth.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H -#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::optimized_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSplit.h b/onert-micro/luci-interpreter/pal/linux/PALSplit.h deleted file mode 100644 index dad048f..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSplit.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPLIT_H -#define LUCI_INTERPRETER_PAL_SPLIT_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Split(const tflite::SplitParams ¶ms, const tflite::RuntimeShape &input_shape, - const Scalar *input_data, const tflite::RuntimeShape *const *output_shapes, - Scalar *const *output_data) -{ - tflite::optimized_ops::Split(params, input_shape, input_data, output_shapes, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPLIT_H diff --git a/onert-micro/luci-interpreter/pal/linux/PALSub.h b/onert-micro/luci-interpreter/pal/linux/PALSub.h deleted file mode 100644 index 1babe1a..0000000 --- a/onert-micro/luci-interpreter/pal/linux/PALSub.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SUB_H -#define LUCI_INTERPRETER_PAL_SUB_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Sub(const tflite::ArithmeticParams ¶ms, - const tflite::RuntimeShape &input1_shape, const T *input1_data, - const tflite::RuntimeShape &input2_shape, const T *input2_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::optimized_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, - output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SUB_H diff --git a/onert-micro/luci-interpreter/pal/linux/pal.cmake b/onert-micro/luci-interpreter/pal/linux/pal.cmake deleted file mode 100644 index c373b96..0000000 --- a/onert-micro/luci-interpreter/pal/linux/pal.cmake +++ /dev/null @@ -1,82 +0,0 @@ -macro(initialize_pal) - nnas_find_package(TensorFlowSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowEigenSource EXACT 2.6.0 QUIET) - nnas_find_package(TensorFlowRuySource EXACT 2.6.0 QUIET) - - if (NOT TensorFlowSource_FOUND) - message(STATUS "Skipping luci-interpreter: TensorFlow not found") - return() - endif () - - if (NOT TensorFlowGEMMLowpSource_FOUND) - message(STATUS "Skipping luci-interpreter: gemmlowp not found") - return() - endif () - - if (NOT TensorFlowEigenSource_FOUND) - message(STATUS "Skipping luci-interpreter: Eigen not found") - return() - endif () - - if (NOT TensorFlowRuySource_FOUND) - message(STATUS "Skipping luci-interpreter: Ruy not found") - return() - endif () - - find_package(Threads REQUIRED) - - set(PAL_INITIALIZED TRUE) -endmacro() - -macro(add_pal_to_target TGT) - target_include_directories(${TGT} PRIVATE "${PAL}") - target_include_directories(${TGT} SYSTEM PRIVATE - "${TensorFlowRuySource_DIR}" - "${TensorFlowGEMMLowpSource_DIR}" - "${TensorFlowEigenSource_DIR}" - "${TensorFlowSource_DIR}") - target_include_directories(${TGT} PRIVATE ${LUCI_INTERPRETER_PAL_DIR}) - - # TODO put it back, I changed my mind. - # instead add sources with visitors in this library - set(PAL_SOURCES ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/tensor_utils.cc - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc) - - if(BUILD_ARM32_NEON) - # NOTE may need to revise this list for version upgrade - set(PAL_SOURCES ${PAL_SOURCES} - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/optimized/cpu_check.cc - ${TensorFlowRuySource_DIR}/ruy/allocator.cc - ${TensorFlowRuySource_DIR}/ruy/block_map.cc - ${TensorFlowRuySource_DIR}/ruy/blocking_counter.cc - ${TensorFlowRuySource_DIR}/ruy/context_get_ctx.cc - ${TensorFlowRuySource_DIR}/ruy/cpuinfo.cc - ${TensorFlowRuySource_DIR}/ruy/ctx.cc - ${TensorFlowRuySource_DIR}/ruy/denormal.cc - ${TensorFlowRuySource_DIR}/ruy/frontend.cc - ${TensorFlowRuySource_DIR}/ruy/pack_arm.cc - ${TensorFlowRuySource_DIR}/ruy/prepacked_cache.cc - ${TensorFlowRuySource_DIR}/ruy/prepare_packed_matrices.cc - ${TensorFlowRuySource_DIR}/ruy/system_aligned_alloc.cc - ${TensorFlowRuySource_DIR}/ruy/thread_pool.cc - ${TensorFlowRuySource_DIR}/ruy/trmul.cc - ${TensorFlowRuySource_DIR}/ruy/tune.cc - ${TensorFlowRuySource_DIR}/ruy/wait.cc - ${TensorFlowRuySource_DIR}/ruy/kernel_arm32.cc - ) - endif(BUILD_ARM32_NEON) - - add_library(luci_interpreter_linux_pal_micro STATIC ${PAL_SOURCES}) - set_target_properties(luci_interpreter_linux_pal_micro PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_include_directories(luci_interpreter_linux_pal_micro SYSTEM PRIVATE - "${TensorFlowRuySource_DIR}" - "${TensorFlowGEMMLowpSource_DIR}" - "${TensorFlowEigenSource_DIR}" - "${TensorFlowSource_DIR}" - ) - - target_link_libraries(${TGT} PRIVATE Threads::Threads luci_interpreter_linux_pal_micro) -endmacro() diff --git a/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst b/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst index 9203973..4288319 100644 --- a/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst +++ b/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst @@ -1,9 +1,47 @@ +REGISTER_KERNEL(ABS, Abs) +REGISTER_KERNEL(ADD, Add) +REGISTER_KERNEL(AVERAGE_POOL_2D, AveragePool2D) +REGISTER_KERNEL(ARG_MAX, ArgMax) +REGISTER_KERNEL(ARG_MIN, ArgMin) +REGISTER_KERNEL(DIV, Div) +REGISTER_KERNEL(ADD_N, AddN) REGISTER_KERNEL(FULLY_CONNECTED, FullyConnected) REGISTER_KERNEL(CONV_2D, Conv2D) REGISTER_KERNEL(LOGISTIC, Logistic) +REGISTER_KERNEL(GATHER, Gather) +REGISTER_KERNEL(EXP, Exp) +REGISTER_KERNEL(GREATER, Greater) +REGISTER_KERNEL(GREATER_EQUAL, GreaterEqual) REGISTER_KERNEL(EXPAND_DIMS, ExpandDims) +REGISTER_KERNEL(ELU, Elu) +REGISTER_KERNEL(EQUAL, Equal) +REGISTER_KERNEL(FILL, Fill) +REGISTER_KERNEL(PACK, Pack) +REGISTER_KERNEL(PAD, Pad) +REGISTER_KERNEL(PADV2, PadV2) REGISTER_KERNEL(RESHAPE, Reshape) +REGISTER_KERNEL(RELU, Relu) +REGISTER_KERNEL(RELU6, Relu6) +REGISTER_KERNEL(REDUCE_PROD, ReduceCommon) +REGISTER_KERNEL(LESS, Less) +REGISTER_KERNEL(LESS_EQUAL, LessEqual) +REGISTER_KERNEL(LOGICAL_AND, LogicalAnd) +REGISTER_KERNEL(LOGICAL_OR, LogicalOr) +REGISTER_KERNEL(LEAKY_RELU, LeakyRelu) +REGISTER_KERNEL(MUL, Mul) REGISTER_KERNEL(MAX_POOL_2D, MaxPool2D) REGISTER_KERNEL(CONCATENATION, Concatenation) +REGISTER_KERNEL(SHAPE, Shape) +REGISTER_KERNEL(NOT_EQUAL, NotEqual) +REGISTER_KERNEL(SLICE, Slice) +REGISTER_KERNEL(SUB, Sub) +REGISTER_KERNEL(SPLIT, Split) +REGISTER_KERNEL(STRIDED_SLICE, StridedSlice) +REGISTER_KERNEL(SPLIT_V, SplitV) +REGISTER_KERNEL(TANH, Tanh) +REGISTER_KERNEL(TRANSPOSE, Transpose) REGISTER_KERNEL(SOFTMAX, Softmax) +REGISTER_KERNEL(WHILE, While) REGISTER_KERNEL(UNIDIRECTIONAL_SEQUENCE_LSTM, UnidirectionalSequenceLSTM) +REGISTER_KERNEL(RESIZE_BILINEAR, ResizeBilinear) +REGISTER_KERNEL(NEG, Neg) diff --git a/onert-micro/luci-interpreter/pal/mcu/PALAdd.h b/onert-micro/luci-interpreter/pal/mcu/PALAdd.h new file mode 100644 index 0000000..d9d1f78 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/mcu/PALAdd.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_ADD_H +#define LUCI_INTERPRETER_PAL_ADD_H + +#include "PALAddCommon.h" + +namespace luci_interpreter_pal +{ +template <> +inline void Add(const ArithmeticParams &, const int, const int8_t *, const int8_t *, + int8_t *) +{ + assert(false && "Not IMPL yet"); +} + +template <> +inline void Add(const ArithmeticParams &, const int, const int16_t *, const int16_t *, + int16_t *) +{ + assert(false && "Not IMPL yet"); +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_ADD_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALApplyActivationToVector.h b/onert-micro/luci-interpreter/pal/mcu/PALApplyActivationToVector.h deleted file mode 100644 index 55076c2..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALApplyActivationToVector.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2017 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_APPLY_ACTIVATION_TO_VECTOR_H -#define LUCI_INTERPRETER_PAL_APPLY_ACTIVATION_TO_VECTOR_H - -#include -#include -#include -#include - -#include "tensorflow/lite/c/builtin_op_data.h" - -namespace luci_interpreter_pal -{ - -// Dynamic (non-fused) activation functor. perhaps it is worth having -// template instantiation? -// TODO(aselle): Make this more efficient by pulling the switch to conv_eval -// using template inlining. -class ActivationFunctor -{ -public: - explicit ActivationFunctor(TfLiteFusedActivation act) : act_(act) {} - - float operator()(float a) const - { - switch (act_) - { - case kTfLiteActNone: - return a; - case kTfLiteActRelu: - return a < 0.f ? 0.f : a; - case kTfLiteActRelu6: - return std::max(0.f, std::min(a, 6.f)); - case kTfLiteActTanh: - return std::tanh(a); - case kTfLiteActSigmoid: - return 1.0f / (1.0f + std::exp(-a)); - default: - assert(false && "Activation functor is not supported"); - } - } - -private: - TfLiteFusedActivation act_; -}; - -inline void ApplyActivationToVector(const float *vector, int v_size, - TfLiteFusedActivation activation, float *result) -{ - auto activation_func = ActivationFunctor(activation); - for (int v = 0; v < v_size; v++) - { - *result++ = (activation_func)(*vector++); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_APPLY_ACTIVATION_TO_VECTOR_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALArgMax.h b/onert-micro/luci-interpreter/pal/mcu/PALArgMax.h deleted file mode 100644 index 84147b2..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALArgMax.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_ARGMAX_H -#define LUCI_INTERPRETER_PAL_ARGMAX_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void ArgMinMax(const tflite::RuntimeShape &input1_shape, const T1 *input1_data, - const T2 *axis, const tflite::RuntimeShape &output_shape, - T3 *output_data, const std::greater cmp) -{ - tflite::reference_ops::ArgMinMax(input1_shape, input1_data, axis, output_shape, output_data, cmp); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_ARGMAX_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2D.h b/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2D.h new file mode 100644 index 0000000..ef5fe72 --- /dev/null +++ b/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2D.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_H +#define LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_H + +#include "PALAveragePool2DCommon.h" + +namespace luci_interpreter_pal +{ +// TODO: add S8 and S16 kernel +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_AVERAGE_POOL_2D_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h b/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h deleted file mode 100644 index fe93b67..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALAveragePool2d.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H -#define LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H - -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data, - const tflite::RuntimeShape &scratchpad_shape, T *scratchpad_data) -{ - { - // MARK: At this moment this operation doesn't support - assert(false && "AveragePool NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void AveragePool(const tflite::PoolParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, - int8_t *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - - tflite::reference_integer_ops::AveragePool(params, input_shape, input_data, output_shape, - output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &output_shape) - -{ - (void)input_data_type; - (void)input_shape; - (void)output_shape; - - scratchpad->set_allocatable(false); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_AVERAGEPOOL2D_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h b/onert-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h deleted file mode 100644 index 91f9277..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALBatchToSpaceND.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H -#define LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -BatchToSpaceND(const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32_t *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32_t *crops_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::BatchToSpaceND( - unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, crops_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_BATCHTOSPACEND_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALConv2d.h b/onert-micro/luci-interpreter/pal/mcu/PALConv2d.h index 03f01b5..c979f76 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALConv2d.h +++ b/onert-micro/luci-interpreter/pal/mcu/PALConv2d.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. 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. @@ -17,70 +17,16 @@ #ifndef LUCI_INTERPRETER_PAL_CONV2D_H #define LUCI_INTERPRETER_PAL_CONV2D_H - -#include -#include +#include "PALConv2DCommon.h" namespace luci_interpreter_pal { -static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, - const float *input_data, const tflite::RuntimeShape &filter_shape, - const float *filter_data, const tflite::RuntimeShape &bias_shape, - const float *bias_data, const tflite::RuntimeShape &output_shape, - float *output_data, const tflite::RuntimeShape &scratchpad_shape, - float *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data, - tflite::RuntimeShape(), nullptr); -} - -static inline void Conv(const tflite::ConvParams ¶ms, const tflite::RuntimeShape &input_shape, - const uint8_t *input_data, const tflite::RuntimeShape &filter_shape, - const uint8_t *filter_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, const tflite::RuntimeShape &output_shape, - uint8_t *output_data, const tflite::RuntimeShape &scratchpad_shape, - uint8_t *scratchpad_data) +static inline void QuantizedConvPerChannel(const ConvParams &, const int32_t *, const int8_t *, + const int32_t *, const int8_t *, const int32_t *, + const int32_t *, int8_t *) { - (void)scratchpad_shape; - (void)scratchpad_data; - tflite::reference_ops::Conv(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data, scratchpad_shape, - scratchpad_data, nullptr); + assert(false && "Not supported yet"); } - -static inline void -ConvPerChannel(const tflite::ConvParams ¶ms, const int32_t *mult, const int32_t *shifts, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - tflite::reference_integer_ops::ConvPerChannel(params, mult, shifts, input_shape, input_data, - filter_shape, filter_data, bias_shape, bias_data, - output_shape, output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const luci_interpreter::DataType &input_data_type, - const tflite::ConvParams ¶ms, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &filter_shape, - const tflite::RuntimeShape &output_shape) -{ - (void)input_data_type; - (void)params; - (void)input_shape; - (void)filter_shape; - (void)output_shape; - (void)scratchpad; -} - } // namespace luci_interpreter_pal #endif // LUCI_INTERPRETER_PAL_CONV2D_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h b/onert-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h deleted file mode 100644 index ce5be43..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALDepthToSpace.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H -#define LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void DepthToSpace(const tflite::DepthToSpaceParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::DepthToSpace(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHTOSPACE_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h b/onert-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h deleted file mode 100644 index 1cba996..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALDepthwiseConv2d.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H -#define LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H - -#include -#include -#include - -namespace luci_interpreter_pal -{ -template -static inline void -DepthwiseConvPerChannel(const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, - const T *input_data, const tflite::RuntimeShape &filter_shape, - const T *filter_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, const tflite::RuntimeShape &output_shape, - T *output_data, const tflite::RuntimeShape &scratchpad_shape, - T *scratchpad_data) -{ - { - // MARK: At this moment this operation is not supported - assert(false && "DepthwiseConvPerChannel NYI"); - (void)params; - (void)output_multiplier; - (void)output_shift; - (void)input_shape; - (void)output_data; - (void)input_data; - (void)filter_shape; - (void)filter_data; - (void)bias_shape; - (void)bias_data; - (void)output_shape; - (void)output_data; - (void)scratchpad_shape; - (void)scratchpad_data; - } -} - -template <> -inline void DepthwiseConvPerChannel( - const tflite::DepthwiseParams ¶ms, const int32_t *output_multiplier, - const int32_t *output_shift, const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, - const tflite::RuntimeShape &scratchpad_shape, int8_t *scratchpad_data) -{ - (void)scratchpad_shape; - (void)scratchpad_data; - tflite::reference_integer_ops::DepthwiseConvPerChannel( - params, output_multiplier, output_shift, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data); -} - -static inline void SetupScratchpadTensor(luci_interpreter::Tensor *scratchpad, - const tflite::DepthwiseParams ¶ms, - const luci_interpreter::DataType &input_data_type, - const tflite::RuntimeShape &input_shape, - const tflite::RuntimeShape &filter_shape, - const tflite::RuntimeShape &output_shape) - -{ - (void)params; - (void)input_data_type; - (void)input_shape; - (void)filter_shape; - (void)output_shape; - - scratchpad->set_allocatable(false); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEPTHWISECONV2D_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALDequantize.h b/onert-micro/luci-interpreter/pal/mcu/PALDequantize.h deleted file mode 100644 index a11abef..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALDequantize.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_DEQUANTIZE_H -#define LUCI_INTERPRETER_PAL_DEQUANTIZE_H - -#include "tensorflow/lite/kernels/internal/reference/integer_ops/dequantize.h" -#include "PALreference_ops.h" - -namespace luci_interpreter_pal -{ - -template -static inline void Dequantize(tflite::DequantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_integer_ops::Dequantize(params, input_shape, input_data, output_shape, - output_data); -} - -static inline void Dequantize(tflite::DequantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const uint8_t *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::Dequantize(params, input_shape, input_data, output_shape, output_data); -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_DEQUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALFullyConnected.h b/onert-micro/luci-interpreter/pal/mcu/PALFullyConnected.h index 25d4dcf..4a024b1 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALFullyConnected.h +++ b/onert-micro/luci-interpreter/pal/mcu/PALFullyConnected.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. 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. @@ -15,48 +15,41 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_PAL_FULLYCONNECTED_H -#define LUCI_INTERPRETER_PAL_FULLYCONNECTED_H +#ifndef LUCI_INTERPRETER_PAL_FULLY_CONNECTED_H +#define LUCI_INTERPRETER_PAL_FULLY_CONNECTED_H -#include -#include +#include "PALFullyConnectedCommon.h" namespace luci_interpreter_pal { -template -static inline void FullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &filter_shape, const T *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, T *output_data) + +template <> +inline void +FullyConnected(const luci_interpreter_pal::FullyConnectedParams ¶ms, const int32_t *input_shape, + const int8_t *input_data, const int32_t *filter_shape, const int8_t *filter_data, + const int32_t *bias_data, const int32_t *output_shape, int8_t *output_data) { - { - // MARK: At this moment this operation is not supported - assert(false && "FullyConnected NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)filter_shape; - (void)filter_data; - (void)bias_shape; - (void)bias_data; - (void)output_shape; - (void)output_data; - } + // MARK: At this moment this operation doesn't support + assert(false && "FullyConnected INT8 NYI"); + (void)params; + (void)input_shape; + (void)input_data; + (void)filter_shape; + (void)filter_data; + (void)bias_data; + (void)output_shape; + (void)output_data; } template <> -inline void -FullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data) +inline void FullyConnected(const luci_interpreter_pal::FullyConnectedParams &, const int32_t *, + const int16_t *, const int32_t *, const int8_t *, const int64_t *, + const int32_t *, int16_t *) { - tflite::reference_integer_ops::FullyConnected(params, input_shape, input_data, filter_shape, - filter_data, bias_shape, bias_data, output_shape, - output_data); + // MARK: At this moment this operation doesn't support + assert(false && "FullyConnected INT8 NYI"); } + } // namespace luci_interpreter_pal -#endif // LUCI_INTERPRETER_PAL_FULLYCONNECTED_H +#endif // LUCI_INTERPRETER_PAL_FULLY_CONNECTED_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALL2Normalize.h b/onert-micro/luci-interpreter/pal/mcu/PALL2Normalize.h deleted file mode 100644 index 16bc4ae..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALL2Normalize.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_L2NORMALIZE_H -#define LUCI_INTERPRETER_PAL_L2NORMALIZE_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void L2Normalization(const tflite::L2NormalizationParams &op_params, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::L2Normalization(op_params, input_shape, input_data, output_shape, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_L2NORMALIZE_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h b/onert-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h index aa3924e..38a302f 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h +++ b/onert-micro/luci-interpreter/pal/mcu/PALL2Pool2D.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h b/onert-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h deleted file mode 100644 index 97daddc..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALLeakyRelu.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_LEAKYRELU_H -#define LUCI_INTERPRETER_PAL_LEAKYRELU_H - -#include - -namespace luci_interpreter_pal -{ -static inline void LeakyRelu(const tflite::LeakyReluParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - tflite::reference_ops::LeakyRelu(params, input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_LEAKYRELU_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALMaxPool2D.h b/onert-micro/luci-interpreter/pal/mcu/PALMaxPool2D.h new file mode 100644 index 0000000..a0fff0c --- /dev/null +++ b/onert-micro/luci-interpreter/pal/mcu/PALMaxPool2D.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LUCI_INTERPRETER_PAL_MAX_POOL_2D_H +#define LUCI_INTERPRETER_PAL_MAX_POOL_2D_H + +#include "PALMaxPool2DCommon.h" + +namespace luci_interpreter_pal +{ +// TODO: Add INT8, INT16 kernels +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_MAX_POOL_2D_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALMul.h b/onert-micro/luci-interpreter/pal/mcu/PALMul.h index e98d8a2..7b55cd1 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALMul.h +++ b/onert-micro/luci-interpreter/pal/mcu/PALMul.h @@ -18,29 +18,25 @@ #ifndef LUCI_INTERPRETER_PAL_MUL_H #define LUCI_INTERPRETER_PAL_MUL_H -#include +#include "PALMulCommon.h" namespace luci_interpreter_pal { -template -static inline void Mul(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, - const T *input1_data, const tflite::RuntimeShape &input2_shape, - const T *input2_data, const tflite::RuntimeShape &output_shape, - T *output_data) + +template <> +inline void Mul(const ArithmeticParams &, const int, const int8_t *, const int8_t *, + int8_t *) { - tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, - input2_data, output_shape, output_data); + assert(false && "Not IMPL yet"); } -template -static inline void -BroadcastMul4DSlow(tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, - const T *input1_data, const tflite::RuntimeShape &input2_shape, - const T *input2_data, const tflite::RuntimeShape &output_shape, T *output_data) +template <> +inline void Mul(const ArithmeticParams &, const int, const int16_t *, const int16_t *, + int16_t *) { - tflite::reference_ops::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, - input2_data, output_shape, output_data); + assert(false && "Not IMPL yet"); } + } // namespace luci_interpreter_pal #endif // LUCI_INTERPRETER_PAL_MUL_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALNeg.h b/onert-micro/luci-interpreter/pal/mcu/PALNeg.h deleted file mode 100644 index b4e430e..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALNeg.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_NEG_H -#define LUCI_INTERPRETER_PAL_NEG_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Negate(const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::Negate(input_shape, input_data, output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_NEG_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALQuantize.h b/onert-micro/luci-interpreter/pal/mcu/PALQuantize.h deleted file mode 100644 index 5d9ea51..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALQuantize.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_QUANTIZE_H -#define LUCI_INTERPRETER_PAL_QUANTIZE_H - -#include "PALreference_ops.h" - -namespace luci_interpreter_pal -{ -template -static inline void Quantize(tflite::QuantizationParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::AffineQuantize(params, input_shape, input_data, output_shape, output_data); -} - -template -static inline void Requantize(const Input *input_data, int32_t size, - int32_t effective_scale_multiplier, int32_t effective_scale_shift, - int32_t input_zero_point, int32_t output_zero_point, - Output *output_data) -{ - tflite::reference_ops::Requantize(input_data, size, effective_scale_multiplier, - effective_scale_shift, input_zero_point, output_zero_point, - output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_QUANTIZE_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h b/onert-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h deleted file mode 100644 index ce9fc80..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALResizeBilinear.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H -#define LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeBilinear(const tflite::ResizeBilinearParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, const int32_t *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::ResizeBilinear(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZEBILINEAR_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h b/onert-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h deleted file mode 100644 index 10a38c0..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALResizeNearestNeighbor.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H -#define LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -ResizeNearestNeighbor(const tflite::ResizeNearestNeighborParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, const T *input_data, - const tflite::RuntimeShape &output_size_shape, - const int32_t *output_size_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::ResizeNearestNeighbor(op_params, unextended_input_shape, input_data, - output_size_shape, output_size_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_RESIZENEARESTNEIGHBOR_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALSVDF.h b/onert-micro/luci-interpreter/pal/mcu/PALSVDF.h deleted file mode 100644 index d39a2f1..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALSVDF.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2020 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SVDF_H -#define LUCI_INTERPRETER_PAL_SVDF_H - -#include - -namespace luci_interpreter_pal -{ -static inline void -IntegerSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const int8_t *input_data, const tflite::RuntimeShape &weight_feature_shape, - const int8_t *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const int16_t *weight_time_data, const tflite::RuntimeShape &bias_shape, - const int32_t *bias_data, int16_t *activation_state_data, - const tflite::RuntimeShape &output_shape, int8_t *output_data, int32_t *scratchpad_data, - int32_t *output_temp_data, int32_t scale_1_a, int scale_1_b, int32_t scale_2_a, - int scale_2_b, int32_t input_zp, int32_t output_zp) -{ - const int n_rank = params.rank; - const int n_batch = input_shape.Dims(0); - const int n_input = input_shape.Dims(1); - const int n_filter = weight_feature_shape.Dims(0); - const int n_unit = n_filter / n_rank; - const int n_memory = weight_time_shape.Dims(1); - - // Left shift the activation_state. - { - int16_t *new_state_start = activation_state_data; - const int16_t *old_state_start = activation_state_data + 1; - const int16_t *old_state_end = activation_state_data + n_batch * n_filter * n_memory; - while (old_state_start != old_state_end) - { - *new_state_start++ = *old_state_start++; - } - } - - // Note: no need to clear the latest activation, matmul is not accumulative. - - // Feature matmul. - { - const int32_t output_max = std::numeric_limits::max(); - const int32_t output_min = std::numeric_limits::min(); - int16_t *result_in_batch = activation_state_data + (n_memory - 1); - for (int b = 0; b < n_batch; b++) - { - const int8_t *matrix_ptr = weight_feature_data; - for (int r = 0; r < n_filter; r++) - { - int32_t dot_prod = 0; - const int8_t *vector_in_batch = input_data + b * n_input; - for (int c = 0; c < n_input; c++) - { - dot_prod += *matrix_ptr++ * (*vector_in_batch++ - input_zp); - } - dot_prod = tflite::MultiplyByQuantizedMultiplier(dot_prod, scale_1_a, scale_1_b); - dot_prod = std::min(std::max(output_min, dot_prod), output_max); - // This assumes state is symmetrically quantized. Otherwise last bit of - // state should be initialized to its zero point and accumulate the - // dot_prod. - // Equivalent as the following: - // result_in_batch = zero point, which happens to be zero. - // result_in_batch += dot_prod_56. - *result_in_batch = dot_prod; - result_in_batch += n_memory; - } - } - } - - // Time. - { - for (int b = 0; b < n_batch; ++b) - { - int32_t *scratch_ptr_batch = scratchpad_data + b * n_filter; - - // Perform batched vector dot product: - const int16_t *vector1_ptr = weight_time_data; - const int16_t *vector2_ptr = activation_state_data + b * n_memory * n_filter; - - for (int i = 0; i < n_filter; i++) - { - *scratch_ptr_batch = 0; - for (int j = 0; j < n_memory; j++) - { - *scratch_ptr_batch += *vector1_ptr++ * *vector2_ptr++; - } - scratch_ptr_batch++; - } - } - } - - // Reduce, add bias, rescale, activation. - { - // Add bias. - if (bias_data) - { - // Vector batch assign: - for (int i = 0; i < n_batch; ++i) - { - int32_t *output_ptr = output_temp_data + i * n_unit; - const int32_t *bias_ptr = bias_data; - for (int j = 0; j < n_unit; ++j) - { - *output_ptr++ = *bias_ptr++; - } - } - } - else - { - int32_t *output_ptr = output_temp_data; - for (int i = 0; i < n_batch * n_unit; ++i) - { - *output_ptr++ = 0; - } - } - - // Reduce. - for (int b = 0; b < n_batch; ++b) - { - int32_t *output_temp_ptr = output_temp_data + b * n_unit; - int32_t *scratch_ptr_batch = scratchpad_data + b * n_filter; - - // Reduction sum vector - for (int i = 0; i < n_unit; ++i) - { - for (int j = 0; j < n_rank; ++j) - { - output_temp_ptr[i] += *scratch_ptr_batch++; - } - } - } - - // Rescale. - const int32_t output_max = std::numeric_limits::max(); - const int32_t output_min = std::numeric_limits::min(); - for (int i = 0; i < n_batch * n_unit; ++i) - { - int32_t x1 = output_temp_data[i]; - int32_t x2 = tflite::MultiplyByQuantizedMultiplier(x1, scale_2_a, scale_2_b); - int32_t x3 = x2 + output_zp; - int32_t x4 = std::min(std::max(output_min, x3), output_max); - output_data[i] = static_cast(x4); - } - } -} -static inline void -FloatSVDF(const TfLiteSVDFParams ¶ms, const tflite::RuntimeShape &input_shape, - const float *input_data, const tflite::RuntimeShape &weight_feature_shape, - const float *weight_feature_data, const tflite::RuntimeShape &weight_time_shape, - const float *weight_time_data, const tflite::RuntimeShape &bias_shape, - const float *bias_data, float *scratchpad_data, float *activation_state_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - const int32_t rank = params.rank; - const int32_t batch_size = input_shape.Dims(0); - const int32_t input_size = input_shape.Dims(1); - const int32_t num_filters = weight_feature_shape.Dims(0); - const int32_t num_units = num_filters / rank; - const int32_t memory_size = weight_time_shape.Dims(1); - - // Left shift the activation_state. - { - float *new_state_start = activation_state_data; - const float *old_state_start = activation_state_data + 1; - const float *old_state_end = activation_state_data + batch_size * num_filters * memory_size; - while (old_state_start != old_state_end) - { - *new_state_start++ = *old_state_start++; - } - } - - // Note: no need to clear the latest activation, matmul is not accumulative. - - // Compute conv1d(inputs, weights_feature). - // The activation_state's rightmost column is used to save current cycle - // activation. This is achieved by starting at state_ptr[memory_size - 1] and - // having the stride equal to memory_size. - - // Perform batched matrix vector multiply operation: - { - const float *matrix = weight_feature_data; - const float *vector = input_data; - float *result = &activation_state_data[memory_size - 1]; - float *result_in_batch = result; - for (int i = 0; i < batch_size; ++i) - { - const float *matrix_ptr = matrix; - for (int j = 0; j < num_filters; ++j) - { - float dot_prod = 0.0f; - const float *vector_in_batch = vector + i * input_size; - for (int k = 0; k < input_size; ++k) - { - dot_prod += *matrix_ptr++ * *vector_in_batch++; - } - *result_in_batch = dot_prod; - result_in_batch += memory_size; - } - } - } - - tflite::reference_ops::ApplyTimeWeightsBiasAndActivation( - batch_size, memory_size, num_filters, num_units, rank, weight_time_data, bias_data, - params.activation, activation_state_data, scratchpad_data, output_data); -} - -static inline void SetupScratchpadTensor( - const luci_interpreter::DataType &input_data_type, - const luci_interpreter::DataType &weight_feature_data_type, - luci_interpreter::Tensor *scratchpad_1, luci_interpreter::Tensor *scratchpad_2, - luci_interpreter::Tensor *scratchpad_3, luci_interpreter::Tensor *scratchpad_4, - luci_interpreter::Tensor *scratchpad_5, luci_interpreter::Tensor *scratchpad_6, - const luci_interpreter::Shape input_shape, const luci_interpreter::Shape weight_time_shape, - const int32_t batch_size, const int32_t num_filters, const int32_t num_units) -{ - - if (input_data_type == luci_interpreter::DataType::FLOAT32 && - (weight_feature_data_type == luci_interpreter::DataType::S8 || - weight_feature_data_type == luci_interpreter::DataType::U8)) - { - (void)input_shape; - (void)weight_time_shape; - (void)scratchpad_3; - (void)scratchpad_4; - (void)scratchpad_5; - (void)scratchpad_6; - - assert(false && "Hybrid type is not currently supported for mcu platform"); - } - - // Resize scratchpad_1 tensor - scratchpad_1->resize({batch_size, num_filters}); - - if (input_data_type == luci_interpreter::DataType::S8) - { - // Resize scratchpad_2 for full_integer op - scratchpad_2->resize({batch_size, num_units}); - } -} - -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SVDF_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALSoftmax.h b/onert-micro/luci-interpreter/pal/mcu/PALSoftmax.h deleted file mode 100644 index cecf38f..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALSoftmax.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SOFTMAX_H -#define LUCI_INTERPRETER_PAL_SOFTMAX_H - -#include - -namespace luci_interpreter_pal -{ -static inline void PopulateSoftmaxLookupTable(tflite::SoftmaxParams *data, float input_scale, - float beta) -{ - // Do nothing for mcu - (void)data; - (void)input_scale; - (void)beta; -} - -static inline void InitializeParams(tflite::SoftmaxParams *params, float input_scale, float beta) -{ - // TODO Impl it - assert(false && "Softmax NYI"); - (void)params; - (void)input_scale; - (void)beta; -} - -template -static inline void Softmax(const tflite::SoftmaxParams ¶ms, - const tflite::RuntimeShape &input_shape, const T *input_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - // TODO Impl it - // MARK: At this moment this operation doesn't support on mcu - assert(false && "Softmax NYI"); - (void)params; - (void)input_shape; - (void)input_data; - (void)output_shape; - (void)output_data; -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SOFTMAX_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h b/onert-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h deleted file mode 100644 index b2df23c..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALSpaceToBatchND.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETOBATCHND_H -#define LUCI_INTERPRETER_PAL_SPACETOBATCHND_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void -SpaceToBatchND(const tflite::SpaceToBatchParams ¶ms, - const tflite::RuntimeShape &unextended_input1_shape, const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, const int32_t *block_shape_data, - const tflite::RuntimeShape &unextended_input3_shape, const int32_t *paddings_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::SpaceToBatchND( - params, unextended_input1_shape, input1_data, unextended_input2_shape, block_shape_data, - unextended_input3_shape, paddings_data, unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETOBATCHND_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h b/onert-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h deleted file mode 100644 index 7866cc2..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALSpaceToDepth.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SPACETODEPTH_H -#define LUCI_INTERPRETER_PAL_SPACETODEPTH_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void SpaceToDepth(const tflite::SpaceToDepthParams &op_params, - const tflite::RuntimeShape &unextended_input_shape, - const T *input_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data) -{ - tflite::reference_ops::SpaceToDepth(op_params, unextended_input_shape, input_data, - unextended_output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SPACETODEPTH_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALSub.h b/onert-micro/luci-interpreter/pal/mcu/PALSub.h deleted file mode 100644 index 2269bc3..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALSub.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_SUB_H -#define LUCI_INTERPRETER_PAL_SUB_H - -#include - -namespace luci_interpreter_pal -{ -template -static inline void Sub(const tflite::ArithmeticParams ¶ms, - const tflite::RuntimeShape &input1_shape, const T *input1_data, - const tflite::RuntimeShape &input2_shape, const T *input2_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - tflite::reference_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, - output_shape, output_data); -} -} // namespace luci_interpreter_pal - -#endif // LUCI_INTERPRETER_PAL_SUB_H diff --git a/onert-micro/luci-interpreter/pal/mcu/PALUnidirectionalSequenceLSTM.h b/onert-micro/luci-interpreter/pal/mcu/PALUnidirectionalSequenceLSTM.h index 287f303..35592ac 100644 --- a/onert-micro/luci-interpreter/pal/mcu/PALUnidirectionalSequenceLSTM.h +++ b/onert-micro/luci-interpreter/pal/mcu/PALUnidirectionalSequenceLSTM.h @@ -18,614 +18,18 @@ #ifndef LUCI_INTERPRETER_PAL_UNIDIRECTIONAL_SEQUENCE_LSTM_H #define LUCI_INTERPRETER_PAL_UNIDIRECTIONAL_SEQUENCE_LSTM_H -#include "kernels/UnidirectionalSequenceLSTM.h" -#include "tensorflow/lite/kernels/internal/reference/fully_connected.h" -#include "tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h" -#include "tensorflow/lite/kernels/internal/reference/logistic.h" -#include "tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h" -#include "tensorflow/lite/kernels/internal/reference/tanh.h" +#include "PALUnidirectionalSequenceLSTMCommon.h" namespace luci_interpreter_pal { -namespace lstm_internal -{ -namespace -{ -// Possible fused activation functions. -typedef enum -{ - kTfLiteActNone = 0, - kTfLiteActRelu, - kTfLiteActReluN1To1, // min(max(-1, x), 1) - kTfLiteActRelu6, // min(max(0, x), 6) - kTfLiteActTanh, - kTfLiteActSignBit, - kTfLiteActSigmoid, -} TfLiteFusedActivation; - -} // namespace - -template -inline T activationFunctionWithMinMax(T x, T output_activation_min, T output_activation_max) -{ - using std::max; - using std::min; - return min(max(x, output_activation_min), output_activation_max); -} - -template -inline void mul(const luci_interpreter::lstm::ArithmeticParams *params, - const tflite::RuntimeShape &input1_shape, const T *input1_data, - const tflite::RuntimeShape &input2_shape, const T *input2_data, - const tflite::RuntimeShape &output_shape, T *output_data) -{ - T output_activation_min = params->quantized_activation_min; - T output_activation_max = params->quantized_activation_max; - - const int flat_size = input1_shape.FlatSize(); - for (int i = 0; i < flat_size; ++i) - { - output_data[i] = activationFunctionWithMinMax(input1_data[i] * input2_data[i], - output_activation_min, output_activation_max); - } -} - -#ifndef DIS_QUANT -inline int32_t multiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier, int shift) -{ - using gemmlowp::RoundingDivideByPOT; - using gemmlowp::SaturatingRoundingDoublingHighMul; - int left_shift = shift > 0 ? shift : 0; - int right_shift = shift > 0 ? 0 : -shift; - return RoundingDivideByPOT( - SaturatingRoundingDoublingHighMul(x * (1 << left_shift), quantized_multiplier), right_shift); -} - -template -void fullyConnectedInteger(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const InputType *input_data, - const tflite::RuntimeShape &filter_shape, const WeightType *filter_data, - const tflite::RuntimeShape &bias_shape, const BiasType *bias_data, - const tflite::RuntimeShape &output_shape, OutputType *output_data) -{ - const int32_t input_offset = params.input_offset; - const int32_t filter_offset = params.weights_offset; - const int32_t output_offset = params.output_offset; - const int32_t output_multiplier = params.output_multiplier; - const int output_shift = params.output_shift; - const int32_t output_activation_min = params.quantized_activation_min; - const int32_t output_activation_max = params.quantized_activation_max; - TFLITE_DCHECK_GE(filter_shape.DimensionsCount(), 2); - TFLITE_DCHECK_GE(output_shape.DimensionsCount(), 1); - - TFLITE_DCHECK_LE(output_activation_min, output_activation_max); - const int filter_dim_count = filter_shape.DimensionsCount(); - const int output_dim_count = output_shape.DimensionsCount(); - const int batches = FlatSizeSkipDim(output_shape, output_dim_count - 1); - const int output_depth = output_shape.Dims(output_dim_count - 1); - TFLITE_DCHECK_LE(output_depth, filter_shape.Dims(filter_dim_count - 2)); - const int accum_depth = filter_shape.Dims(filter_dim_count - 1); - for (int b = 0; b < batches; ++b) - { - for (int out_c = 0; out_c < output_depth; ++out_c) - { - BiasType acc = 0; - for (int d = 0; d < accum_depth; ++d) - { - int32_t input_val = input_data[b * accum_depth + d]; - int32_t filter_val = filter_data[out_c * accum_depth + d]; - acc += (filter_val + filter_offset) * (input_val + input_offset); - } - if (bias_data) - { - acc += bias_data[out_c]; - } - int32_t acc_scaled = multiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); - acc_scaled += output_offset; - acc_scaled = std::max(acc_scaled, output_activation_min); - acc_scaled = std::min(acc_scaled, output_activation_max); - output_data[out_c + output_depth * b] = static_cast(acc_scaled); - } - } -} - -void fullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const int8_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int32_t *bias_data, - const tflite::RuntimeShape &output_shape, int16_t *output_data) -{ - return fullyConnectedInteger(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data); -} - -void fullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const int16_t *input_data, - const tflite::RuntimeShape &filter_shape, const int8_t *filter_data, - const tflite::RuntimeShape &bias_shape, const int64_t *bias_data, - const tflite::RuntimeShape &output_shape, int16_t *output_data) -{ - return fullyConnectedInteger(params, input_shape, input_data, filter_shape, filter_data, - bias_shape, bias_data, output_shape, output_data); -} - -template -void mulElementwise(int size, const luci_interpreter::lstm::ArithmeticParams *params, - const InputType *input1_data, const InputType *input2_data, - OutputType *output_data) -{ - for (int i = 0; i < size; ++i) - { - const int32_t input1_val = params->input1_offset + input1_data[i]; - const int32_t input2_val = params->input2_offset + input2_data[i]; - const int32_t unclamped_result = - params->output_offset + multiplyByQuantizedMultiplier(input1_val * input2_val, - params->output_multiplier, - params->output_shift); - const int32_t clamped_output = - std::min(params->quantized_activation_max, - std::max(params->quantized_activation_min, unclamped_result)); - output_data[i] = static_cast(clamped_output); - } -} - -// Input and output have the same shape in LSTM -void mul(const tflite::RuntimeShape &shape, const luci_interpreter::lstm::ArithmeticParams *params, - const int16_t *input1_data, const int16_t *input2_data, int8_t *output_data) -{ - return mulElementwise(shape.FlatSize(), params, input1_data, input2_data, - output_data); -} - -// Input and output have the same shape in LSTM -void mul(const tflite::RuntimeShape &shape, const luci_interpreter::lstm::ArithmeticParams *params, - const int16_t *input1_data, const int16_t *input2_data, int16_t *output_data) -{ - return mulElementwise(shape.FlatSize(), params, input1_data, input2_data, output_data); -} - -void addElementWise(const int16_t *input_1, const int16_t *input_2, int n_batch, int n_input, - int16_t *output) -{ - for (int batch = 0; batch < n_batch; ++batch) - { - for (int i = 0; i < n_input; ++i) - { - const int index = batch * n_input + i; - int32_t sum = input_1[index] + input_2[index]; - const int32_t sum_clamped = - std::min(static_cast(std::numeric_limits::max()), - std::max(static_cast(std::numeric_limits::min()), sum)); - output[index] = static_cast(sum_clamped); - } - } -} - -void tanh(int32_t cell_state_scale_power, const tflite::RuntimeShape &input_data_shape, - int16_t *input_data, const tflite::RuntimeShape &output_data_shape, int16_t *output_data) -{ - int32_t tanh_input_left_shift = (15 + cell_state_scale_power) - 3; - int32_t input_multiplier = 0; - if (tanh_input_left_shift < 0) /* handling negative shift value */ - { - tanh_input_left_shift = -tanh_input_left_shift; - input_multiplier = 3; - } - tflite::reference_integer_ops::Tanh(input_multiplier, tanh_input_left_shift, input_data_shape, - input_data, output_data_shape, output_data); -} - -void sigmoid(const tflite::RuntimeShape &data_shape, int16_t *data) -{ - tflite::reference_integer_ops::Logistic(0 /*data->input_multiplier*/, - 0 /*data->input_left_shift */, - data_shape.FlatSize() /*NumElements(input->dims)*/, - data /* tflite::micro::GetTensorData(input) */, - data /*tflite::micro::GetTensorData(output) */); -} - -void clipping(const int v_size, const luci_interpreter::lstm::CellStateInfo *cell_state_info, - int16_t *vector) -{ - for (int i = 0; i < v_size; i++) - { - vector[i] = std::max(std::min(cell_state_info->quantized_cell_clip, vector[i]), - static_cast(-cell_state_info->quantized_cell_clip)); - } -} -#endif // DIS_QUANT - -#ifndef DIS_FLOAT -void fullyConnected(const tflite::FullyConnectedParams ¶ms, - const tflite::RuntimeShape &input_shape, const float *input_data, - const tflite::RuntimeShape &filter_shape, const float *filter_data, - const tflite::RuntimeShape &bias_shape, const float *bias_data, - const tflite::RuntimeShape &output_shape, float *output_data) -{ - return tflite::reference_ops::FullyConnected(params, input_shape, input_data, filter_shape, - filter_data, bias_shape, bias_data, output_shape, - output_data); -} - -// Input and output have the same shape in LSTM -void mul(const tflite::RuntimeShape &shape, const luci_interpreter::lstm::ArithmeticParams *params, - const float *input1_data, const float *input2_data, float *output_data) -{ - return mul(params, shape, input1_data, shape, input2_data, shape, output_data); -} - -void addElementWise(const float *input_1, const float *input_2, int n_batch, int n_input, - float *output) -{ - for (int batch = 0; batch < n_batch; ++batch) - { - for (int i = 0; i < n_input; ++i) - { - const int index = batch * n_input + i; - output[index] = input_1[index] + input_2[index]; - } - } -} - -void tanh(int32_t cell_state_scale_power, const tflite::RuntimeShape &input_data_shape, - float *input_data, const tflite::RuntimeShape &output_data_shape, float *output_data) -{ - tflite::reference_ops::Tanh(input_data_shape, input_data, output_data_shape, output_data); -} - -void sigmoid(const tflite::RuntimeShape &data_shape, float *data) -{ - tflite::reference_ops::Logistic(data_shape, data, data_shape, data); -} - -void clipping(const int v_size, const luci_interpreter::lstm::CellStateInfo *cell_state_info, - float *vector) -{ - for (int i = 0; i < v_size; i++) - { - vector[i] = - std::max(std::min(cell_state_info->cell_clip, vector[i]), -cell_state_info->cell_clip); - } -} -#endif // DIS_FLOAT - -// Size information about the LSTM kernel, which is deduced from tensors stored -// in the flat buffer file. -struct LstmSizeInfo -{ - bool time_major; - int32_t batch_size; - int32_t time_steps; - int32_t input_dimension; - int32_t state_dimension; -}; - -class LstmStepManager -{ -public: - LstmStepManager() = delete; - // Does not take any ownership, and all pointers must refer to valid objects - // that outlive the one constructed. - explicit LstmStepManager(const LstmSizeInfo &size_info) : size_info_(size_info) {} - - void updateTime() - { - current_time_ += 1; - // default as one batch per inference - int input_step = size_info_.input_dimension; - int output_step = size_info_.state_dimension; - // time major: batch inference - if (size_info_.time_major) - { - input_step = input_step * size_info_.batch_size; - output_step = output_step * size_info_.batch_size; - } - - input_offset_ += input_step; - output_offset_ += output_step; - } - - void updateBatch() - { - current_batch_ += 1; - TFLITE_DCHECK_LE(current_batch_, size_info_.batch_size); - // batch inference for time major: no action needed - if (size_info_.time_major) - { - return; - } - // otherwise: singe batch inference, go to the next batch - hidden_state_offset_ += size_info_.state_dimension; - cell_state_offset_ += size_info_.state_dimension; - } - - void resetTime() { current_time_ = 0; } - - tflite::RuntimeShape inputShape() const - { - int batch_size = 1; - if (size_info_.time_major) - { - batch_size = size_info_.batch_size; - } - const int dims[2] = {batch_size, size_info_.input_dimension}; - const int32_t *dims_data = reinterpret_cast(dims); - return tflite::RuntimeShape(2, dims_data); - } - - tflite::RuntimeShape stateShape() const - { - int batch_size = 1; - if (size_info_.time_major) - { - batch_size = size_info_.batch_size; - } - const int dims[2] = {batch_size, size_info_.state_dimension}; - const int32_t *dims_data = reinterpret_cast(dims); - return tflite::RuntimeShape(2, dims_data); - } - - int inputOffset() const { return input_offset_; } - - int outputOffset() const { return output_offset_; } - - int hiddenStateOffset() const { return hidden_state_offset_; } - - int cellStateOffset() const { return cell_state_offset_; } - -private: - int32_t current_time_ = 0; - int32_t current_batch_ = 0; - int32_t input_offset_ = 0; - int32_t output_offset_ = 0; - int32_t hidden_state_offset_ = 0; - int32_t cell_state_offset_ = 0; - - const LstmSizeInfo &size_info_; -}; - -// Calculates a single LSTM gate. -// Implements the following formula: -// gate = activate(FC(input) + FC(recurrent)) -// Activation is sigmoid except for the "cell" gate (configurable, usually tanh) -template -void calculateLstmGate(const LstmStepManager *step_info, - const luci_interpreter::lstm::GateParameters *gate_params, - // Input FC - ActivationType *input_data, const circle::Tensor *input_weight, - const circle::Tensor *input_bias, - // Recurrent FC - ActivationType *recurrent_data, const circle::Tensor *recurrent_weight, - const circle::Tensor *recurrent_bias, - // Output - CellType *gate_output, - // Scratch arrays - CellType *fc_output_buffer, const TfLiteFusedActivation activation, - luci_interpreter::BaseRuntimeGraph *runtime_graph) -{ - // Input FC - const auto gate_output_shape = step_info->stateShape(); - { - tflite::FullyConnectedParams op_params{}; - op_params.input_offset = gate_params->input_fc_params.input_offset; - op_params.weights_offset = gate_params->input_fc_params.weights_offset; - op_params.output_offset = gate_params->input_fc_params.output_offset; - op_params.output_multiplier = gate_params->input_fc_params.output_multiplier; - op_params.output_shift = gate_params->input_fc_params.output_shift; - op_params.quantized_activation_min = gate_params->input_fc_params.quantized_activation_min; - op_params.quantized_activation_max = gate_params->input_fc_params.quantized_activation_max; - op_params.float_activation_max = gate_params->input_fc_params.float_activation_max; - op_params.float_activation_min = gate_params->input_fc_params.float_activation_min; - - fullyConnected(op_params, step_info->inputShape(), input_data + step_info->inputOffset(), - luci_interpreter::kernels::getTensorShape(input_weight), - luci_interpreter::kernels::getTensorData( - runtime_graph->getConstDataByTensor(input_weight)), - luci_interpreter::kernels::getTensorShape(input_bias), - luci_interpreter::kernels::getTensorData( - runtime_graph->getConstDataByTensor(input_bias)), - gate_output_shape, gate_output); - } - - // Recurrent FC - { - tflite::FullyConnectedParams op_params{}; - op_params.input_offset = gate_params->recurrent_fc_params.input_offset; - op_params.weights_offset = gate_params->recurrent_fc_params.weights_offset; - op_params.output_offset = gate_params->recurrent_fc_params.output_offset; - op_params.output_multiplier = gate_params->recurrent_fc_params.output_multiplier; - op_params.output_shift = gate_params->recurrent_fc_params.output_shift; - op_params.quantized_activation_min = gate_params->recurrent_fc_params.quantized_activation_min; - op_params.quantized_activation_max = gate_params->recurrent_fc_params.quantized_activation_max; - op_params.float_activation_max = gate_params->recurrent_fc_params.float_activation_max; - op_params.float_activation_min = gate_params->recurrent_fc_params.float_activation_min; - - fullyConnected(op_params, step_info->stateShape(), - recurrent_data + step_info->hiddenStateOffset(), - luci_interpreter::kernels::getTensorShape(recurrent_weight), - luci_interpreter::kernels::getTensorData( - runtime_graph->getConstDataByTensor(recurrent_weight)), - luci_interpreter::kernels::getTensorShape(recurrent_bias), - luci_interpreter::kernels::getTensorData( - runtime_graph->getConstDataByTensor(recurrent_bias)), - gate_output_shape, fc_output_buffer); - - addElementWise(gate_output, fc_output_buffer, /*n_batch=*/gate_output_shape.DimsData()[0], - /*n_state=*/gate_output_shape.DimsData()[1], gate_output); - - switch (activation) - { - case TfLiteFusedActivation::kTfLiteActSigmoid: - sigmoid(gate_output_shape, gate_output); - break; - case TfLiteFusedActivation::kTfLiteActTanh: - { - // Set the scale power to -12 to avoid shift - tanh(/*cell_state_scale_power=*/-12, gate_output_shape, gate_output, gate_output_shape, - gate_output); - } - break; - default: - // Only Sigmoid or Tanh is used. - assert(false && "Only Sigmoid or Tanh is used"); - } - } -} - -// Update the hidden state of the LSTM kernel using the following formula: -// updated_hidden_state = Tanh(updated_cell_state) * output_gate_output, * means -// element wise multiplication -template -void updateLstmHidden(const LstmStepManager *step_info, CellType *cell_state_data_base, - ActivationType *hidden_state_data, const CellType *output_gate_output, - const luci_interpreter::lstm::ArithmeticParams *mul_params, - int32_t cell_state_scale_power, CellType *buffer) -{ - auto cell_state_shape = step_info->stateShape(); - CellType *cell_state_data = cell_state_data_base + step_info->cellStateOffset(); - // Tanh(cell_state) - tanh(cell_state_scale_power, cell_state_shape, cell_state_data, cell_state_shape, buffer); - // Update the hidden state - mul(cell_state_shape, mul_params, buffer, output_gate_output, - hidden_state_data + step_info->hiddenStateOffset()); -} - -// Update the cell state using the output from the forget gate, input gate, and -// cell gate Formula: updated_cell_state = forget_gate_output*cell_state + -// input_gate_output * cell_gate_output, where * denotes element wise -// multiplication -template -void updateLstmCell(const LstmStepManager *step_info, CellType *cell_state_data, - // Gate outputs - CellType *forget_gate_output, const CellType *input_gate_output, - const CellType *cell_gate_output, - // Mul parameters - const luci_interpreter::lstm::ArithmeticParams &forget_cell_mul_params, - const luci_interpreter::lstm::ArithmeticParams &input_mul_params, - const luci_interpreter::lstm::CellStateInfo *cell_state_info, CellType *buffer) -{ - auto cell_state_shape = step_info->stateShape(); - // Forget Gate x Cell State - mul(cell_state_shape, &forget_cell_mul_params, forget_gate_output, - cell_state_data + step_info->cellStateOffset(), - cell_state_data + step_info->cellStateOffset()); - // Input Gate x Cell Gate - mul(cell_state_shape, &input_mul_params, input_gate_output, cell_gate_output, buffer); - - // Update the cell state - addElementWise(cell_state_data + step_info->cellStateOffset(), buffer, - /*n_batch=*/cell_state_shape.DimsData()[0], - /*n_state=*/cell_state_shape.DimsData()[1], - cell_state_data + step_info->cellStateOffset()); - - if (cell_state_info->cell_clip > 0) - { - clipping(cell_state_shape.FlatSize(), cell_state_info, - cell_state_data + step_info->cellStateOffset()); - } -} - -template -void lstmStep(luci_interpreter::lstm::LSTMStruct *lstm_struct, - luci_interpreter::lstm::LSTMParameters *lstm_params, LstmStepManager *step_info, - luci_interpreter::lstm::CellStateInfo *cell_state_info, - ActivationType *output_state_data, CellType *cell_state_data, CellType *scratch0, - CellType *scratch1, CellType *scratch2, CellType *scratch3, - luci_interpreter::BaseRuntimeGraph *runtime_graph) -{ - /*Step1: Calculate gate outputs to prepare cell state update*/ - CellType *gate_internal_buffer = scratch3; - CellType *forget_gate_output = scratch0; - - auto input_data = luci_interpreter::kernels::getTensorData( - runtime_graph->getDataByTensor(lstm_struct->input())); - - calculateLstmGate( - step_info, &lstm_params->forget_gate_parameters, - // Input FC - input_data, lstm_struct->input_to_forget_weights(), lstm_struct->forget_gate_bias(), - // Recurrent FC - output_state_data, lstm_struct->recurrent_to_forget_weights(), nullptr, - // Output - forget_gate_output, gate_internal_buffer, TfLiteFusedActivation::kTfLiteActSigmoid, - runtime_graph); - - // Input Gate calculation; - CellType *input_gate_output = scratch1; - calculateLstmGate( - step_info, &lstm_params->input_gate_parameters, - // Input FC - input_data, lstm_struct->input_to_input_weights(), lstm_struct->input_gate_bias(), - // Recurrent FC - output_state_data, lstm_struct->recurrent_to_input_weights(), - /*recurrent_bias*/ nullptr, - // Output - input_gate_output, - // Scratch arrays - gate_internal_buffer, TfLiteFusedActivation::kTfLiteActSigmoid, runtime_graph); - - // Cell Gate calculation - CellType *cell_gate_output = scratch2; - calculateLstmGate( - step_info, &lstm_params->cell_gate_parameters, - // Input FC - input_data, lstm_struct->input_to_cell_weights(), lstm_struct->cell_gate_bias(), - // Recurrent FC - output_state_data, lstm_struct->recurrent_to_cell_weights(), - /*recurrent_bias*/ nullptr, - // Output - cell_gate_output, - // Scratch arrays - gate_internal_buffer, TfLiteFusedActivation::kTfLiteActTanh, runtime_graph); - - /*Step2: update the cell state */ - { - // const InterGateParameters& inter_gate_params = op_data.inter_gate_parameters; - CellType *updated_input_buffer = scratch1; // reuse buffer - - updateLstmCell( - step_info, cell_state_data, forget_gate_output, input_gate_output, cell_gate_output, - lstm_params->inter_gate_parameters.forget_cell_mul_params, - lstm_params->inter_gate_parameters.input_mul_params, cell_state_info, updated_input_buffer); - } - - { - /*Step3: update the hidden state */ - CellType *output_gate_output = scratch1; // reuse buffer - calculateLstmGate( - step_info, &lstm_params->output_gate_parameters, - // Input FC - input_data, lstm_struct->input_to_output_weights(), lstm_struct->output_gate_bias(), - // Recurrent FC - output_state_data, lstm_struct->recurrent_to_output_weights(), nullptr, - // Output - output_gate_output, - // Scratch arrays - gate_internal_buffer, TfLiteFusedActivation::kTfLiteActSigmoid, runtime_graph); - CellType *tanh_activated_cell_buffer = scratch0; // reuse buffer - updateLstmHidden( - step_info, cell_state_data, output_state_data, output_gate_output, - &lstm_params->inter_gate_parameters.output_mul_params, - cell_state_info->cell_state_scale_power, tanh_activated_cell_buffer); - - ActivationType *output_ptr = luci_interpreter::kernels::getTensorData( - runtime_graph->getDataByTensor(lstm_struct->output())); - std::memcpy(output_ptr + step_info->outputOffset(), - output_state_data + step_info->hiddenStateOffset(), - step_info->stateShape().FlatSize() * sizeof(ActivationType)); - } -} - -} // namespace lstm_internal - // Evaluate the LSTM kernel with (potential) multi-steps and multi-batch input -template -void evalLSTM(luci_interpreter::lstm::LSTMStruct *lstm_struct, - luci_interpreter::lstm::LSTMParameters *lstm_params, - luci_interpreter::lstm::CellStateInfo *cell_state_info, - ActivationType *output_state_data, CellType *cell_state_data, CellType *scratch0, - CellType *scratch1, CellType *scratch2, CellType *scratch3, - luci_interpreter::BaseRuntimeGraph *runtime_graph) +template <> +void evalLSTM( + luci_interpreter::lstm::LSTMStruct *lstm_struct, + luci_interpreter::lstm::LSTMParameters *lstm_params, + luci_interpreter::lstm::CellStateInfo *cell_state_info, int8_t *output_state_data, + int16_t *cell_state_data, int16_t *scratch0, int16_t *scratch1, int16_t *scratch2, + int16_t *scratch3, luci_interpreter::BaseRuntimeGraph *runtime_graph) { lstm_internal::LstmSizeInfo size_info; @@ -646,7 +50,7 @@ void evalLSTM(luci_interpreter::lstm::LSTMStruct *lstm_struct, { for (int t = 0; t < size_info.time_steps; t++) { - lstm_internal::lstmStep( + lstm_internal::lstmStep( lstm_struct, lstm_params, &step_info, cell_state_info, output_state_data, cell_state_data, scratch0, scratch1, scratch2, scratch3, runtime_graph); // prepare for the next time step @@ -660,7 +64,7 @@ void evalLSTM(luci_interpreter::lstm::LSTMStruct *lstm_struct, { for (int t = 0; t < size_info.time_steps; t++) { - lstm_internal::lstmStep( + lstm_internal::lstmStep( lstm_struct, lstm_params, &step_info, cell_state_info, output_state_data, cell_state_data, scratch0, scratch1, scratch2, scratch3, runtime_graph); // prepare for the next time step diff --git a/onert-micro/luci-interpreter/pal/mcu/PALreference_ops.h b/onert-micro/luci-interpreter/pal/mcu/PALreference_ops.h deleted file mode 100644 index 62c7209..0000000 --- a/onert-micro/luci-interpreter/pal/mcu/PALreference_ops.h +++ /dev/null @@ -1,1556 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2017 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_PAL_REFERENCE_OPS_H -#define LUCI_INTERPRETER_PAL_REFERENCE_OPS_H - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "third_party/eigen3/Eigen/Core" -#include "fixedpoint/fixedpoint.h" -#include "ruy/profiler/instrumentation.h" // from @ruy -#include "tensorflow/lite/c/common.h" -#include "tensorflow/lite/kernels/internal/common.h" -#include "tensorflow/lite/kernels/internal/quantization_util.h" -#include "tensorflow/lite/kernels/internal/reference/add.h" -#include "tensorflow/lite/kernels/internal/reference/add_n.h" -#include "tensorflow/lite/kernels/internal/reference/arg_min_max.h" -#include "tensorflow/lite/kernels/internal/reference/batch_matmul.h" -#include "tensorflow/lite/kernels/internal/reference/batch_to_space_nd.h" -#include "tensorflow/lite/kernels/internal/reference/binary_function.h" -#include "tensorflow/lite/kernels/internal/reference/cast.h" -#include "tensorflow/lite/kernels/internal/reference/ceil.h" -#include "tensorflow/lite/kernels/internal/reference/comparisons.h" -#include "tensorflow/lite/kernels/internal/reference/concatenation.h" -#include "tensorflow/lite/kernels/internal/reference/conv.h" -#include "tensorflow/lite/kernels/internal/reference/depth_to_space.h" -#include "tensorflow/lite/kernels/internal/reference/dequantize.h" -#include "tensorflow/lite/kernels/internal/reference/div.h" -#include "tensorflow/lite/kernels/internal/reference/elu.h" -#include "tensorflow/lite/kernels/internal/reference/exp.h" -#include "tensorflow/lite/kernels/internal/reference/fill.h" -#include "tensorflow/lite/kernels/internal/reference/floor.h" -#include "tensorflow/lite/kernels/internal/reference/floor_div.h" -#include "tensorflow/lite/kernels/internal/reference/floor_mod.h" -#include "tensorflow/lite/kernels/internal/reference/fully_connected.h" -#include "tensorflow/lite/kernels/internal/reference/gather.h" -#include "tensorflow/lite/kernels/internal/reference/hard_swish.h" -#include "tensorflow/lite/kernels/internal/reference/l2normalization.h" -#include "tensorflow/lite/kernels/internal/reference/leaky_relu.h" -#include "tensorflow/lite/kernels/internal/reference/log_softmax.h" -#include "tensorflow/lite/kernels/internal/reference/logistic.h" -#include "tensorflow/lite/kernels/internal/reference/maximum_minimum.h" -#include "tensorflow/lite/kernels/internal/reference/mul.h" -#include "tensorflow/lite/kernels/internal/reference/neg.h" -#include "tensorflow/lite/kernels/internal/reference/pad.h" -#include "tensorflow/lite/kernels/internal/reference/pooling.h" -#include "tensorflow/lite/kernels/internal/reference/prelu.h" -#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h" -#include "tensorflow/lite/kernels/internal/reference/quantize.h" -#include "tensorflow/lite/kernels/internal/reference/reduce.h" -#include "tensorflow/lite/kernels/internal/reference/requantize.h" -#include "tensorflow/lite/kernels/internal/reference/resize_bilinear.h" -#include "tensorflow/lite/kernels/internal/reference/resize_nearest_neighbor.h" -#include "tensorflow/lite/kernels/internal/reference/round.h" -#include "tensorflow/lite/kernels/internal/reference/softmax.h" -#include "tensorflow/lite/kernels/internal/reference/space_to_batch_nd.h" -#include "tensorflow/lite/kernels/internal/reference/space_to_depth.h" -#include "tensorflow/lite/kernels/internal/reference/strided_slice.h" -#include "tensorflow/lite/kernels/internal/reference/string_comparisons.h" -#include "tensorflow/lite/kernels/internal/reference/sub.h" -#include "tensorflow/lite/kernels/internal/reference/tanh.h" -#include "tensorflow/lite/kernels/internal/reference/transpose.h" -#include "tensorflow/lite/kernels/internal/reference/transpose_conv.h" -#include "tensorflow/lite/kernels/internal/strided_slice_logic.h" -#include "tensorflow/lite/kernels/internal/tensor.h" -#include "tensorflow/lite/kernels/internal/types.h" -namespace tflite -{ - -namespace reference_ops -{ - -template -inline void Relu(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T lower = 0; - const T clamped = val < lower ? lower : val; - output_data[i] = clamped; - } -} - -template -inline void Relu1(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Relu1 (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T upper = 1; - const T lower = -1; - const T clamped = val > upper ? upper : val < lower ? lower : val; - output_data[i] = clamped; - } -} - -inline void Relu6(const RuntimeShape &input_shape, const float *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - ruy::profiler::ScopeLabel label("Relu6 (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const float val = input_data[i]; - const float upper = 6; - const float lower = 0; - const float clamped = val > upper ? upper : val < lower ? lower : val; - output_data[i] = clamped; - } -} - -template -inline void ReluX(const tflite::ReluParams ¶ms, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - const int32 val = static_cast(input_data[i]); - int32 clamped = params.output_offset + MultiplyByQuantizedMultiplier(val - params.input_offset, - params.output_multiplier, - params.output_shift); - clamped = std::max(params.quantized_activation_min, clamped); - clamped = std::min(params.quantized_activation_max, clamped); - output_data[i] = static_cast(clamped); - } -} - -template -inline void ReluX(const tflite::ActivationParams ¶ms, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - ruy::profiler::ScopeLabel label("Quantized ReluX (not fused)"); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - const T max_value = params.quantized_activation_max; - const T min_value = params.quantized_activation_min; - for (int i = 0; i < flat_size; ++i) - { - const T val = input_data[i]; - const T clamped = val > max_value ? max_value : val < min_value ? min_value : val; - output_data[i] = clamped; - } -} - -// TODO(jiawen): We can implement BroadcastMul on buffers of arbitrary -// dimensionality if the runtime code does a single loop over one dimension -// that handles broadcasting as the base case. The code generator would then -// generate max(D1, D2) nested for loops. -inline void BroadcastMulFivefold(const ArithmeticParams &unswitched_params, - const RuntimeShape &unswitched_input1_shape, - const uint8 *unswitched_input1_data, - const RuntimeShape &unswitched_input2_shape, - const uint8 *unswitched_input2_data, - const RuntimeShape &output_shape, uint8 *output_data) -{ - ArithmeticParams switched_params = unswitched_params; - switched_params.input1_offset = unswitched_params.input2_offset; - switched_params.input2_offset = unswitched_params.input1_offset; - - const bool use_unswitched = unswitched_params.broadcast_category == - tflite::BroadcastableOpCategory::kFirstInputBroadcastsFast; - - const ArithmeticParams ¶ms = use_unswitched ? unswitched_params : switched_params; - const uint8 *input1_data = use_unswitched ? unswitched_input1_data : unswitched_input2_data; - const uint8 *input2_data = use_unswitched ? unswitched_input2_data : unswitched_input1_data; - - // Fivefold nested loops. The second input resets its position for each - // iteration of the second loop. The first input resets its position at the - // beginning of the fourth loop. The innermost loop is an elementwise Mul of - // sections of the arrays. - uint8 *output_data_ptr = output_data; - const uint8 *input1_data_ptr = input1_data; - const uint8 *input2_data_reset = input2_data; - int y0 = params.broadcast_shape[0]; - int y1 = params.broadcast_shape[1]; - int y2 = params.broadcast_shape[2]; - int y3 = params.broadcast_shape[3]; - int y4 = params.broadcast_shape[4]; - for (int i0 = 0; i0 < y0; ++i0) - { - const uint8 *input2_data_ptr; - for (int i1 = 0; i1 < y1; ++i1) - { - input2_data_ptr = input2_data_reset; - for (int i2 = 0; i2 < y2; ++i2) - { - for (int i3 = 0; i3 < y3; ++i3) - { - MulElementwise(y4, params, input1_data_ptr, input2_data_ptr, output_data_ptr); - input2_data_ptr += y4; - output_data_ptr += y4; - } - input1_data_ptr += y4; - } - } - input2_data_reset = input2_data_ptr; - } -} - -inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16 *input1_data, const RuntimeShape &input2_shape, - const int16 *input2_data, const RuntimeShape &output_shape, int16 *output_data) -{ - ruy::profiler::ScopeLabel label("Mul/Int16"); - - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - - for (int i = 0; i < flat_size; i++) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - - F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); - output_data[i] = unclamped_result.raw(); - } -} - -inline void Mul(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16 *input1_data, const RuntimeShape &input2_shape, - const int16 *input2_data, const RuntimeShape &output_shape, uint8 *output_data) -{ - ruy::profiler::ScopeLabel label("Mul/Int16Uint8"); - int32 output_offset = params.output_offset; - int32 output_activation_min = params.quantized_activation_min; - int32 output_activation_max = params.quantized_activation_max; - TFLITE_DCHECK_LE(output_activation_min, output_activation_max); - - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - - for (int i = 0; i < flat_size; i++) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - - F0 unclamped_result = F0::FromRaw(input1_data[i]) * F0::FromRaw(input2_data[i]); - int16 rescaled_result = gemmlowp::RoundingDivideByPOT(unclamped_result.raw(), 8); - int16 clamped_result = std::min(output_activation_max - output_offset, rescaled_result); - clamped_result = std::max(output_activation_min - output_offset, clamped_result); - output_data[i] = output_offset + clamped_result; - } -} - -inline void Sub16(const ArithmeticParams ¶ms, const RuntimeShape &input1_shape, - const int16_t *input1_data, const RuntimeShape &input2_shape, - const int16_t *input2_data, const RuntimeShape &output_shape, - int16_t *output_data) -{ - ruy::profiler::ScopeLabel label("Sub/Int16"); - const int input1_shift = params.input1_shift; - const int flat_size = MatchingElementsSize(input1_shape, input2_shape, output_shape); - const int16 output_activation_min = params.quantized_activation_min; - const int16 output_activation_max = params.quantized_activation_max; - - TFLITE_DCHECK(input1_shift == 0 || params.input2_shift == 0); - TFLITE_DCHECK_LE(input1_shift, 0); - TFLITE_DCHECK_LE(params.input2_shift, 0); - const int16 *not_shift_input = input1_shift == 0 ? input1_data : input2_data; - const int16 *shift_input = input1_shift == 0 ? input2_data : input1_data; - const int input_right_shift = input1_shift == 0 ? -params.input2_shift : -input1_shift; - - if (input1_shift == 0) - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - for (int i = 0; i < flat_size; ++i) - { - F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); - F0 scaled_input = - F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); - F0 result = SaturatingSub(input_ready_scaled, scaled_input); - const int16 raw_output = result.raw(); - const int16 clamped_output = - std::min(output_activation_max, std::max(output_activation_min, raw_output)); - output_data[i] = clamped_output; - } - } - else - { - // F0 uses 0 integer bits, range [-1, 1]. - using F0 = gemmlowp::FixedPoint; - for (int i = 0; i < flat_size; ++i) - { - F0 input_ready_scaled = F0::FromRaw(not_shift_input[i]); - F0 scaled_input = - F0::FromRaw(gemmlowp::RoundingDivideByPOT(shift_input[i], input_right_shift)); - F0 result = SaturatingSub(scaled_input, input_ready_scaled); - const int16 raw_output = result.raw(); - const int16 clamped_output = - std::min(output_activation_max, std::max(output_activation_min, raw_output)); - output_data[i] = clamped_output; - } - } -} - -template -void Pack(const PackParams ¶ms, const RuntimeShape *const *input_shapes, - const Scalar *const *input_data, const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("Pack"); - const int dimensions = output_shape.DimensionsCount(); - int axis = params.axis; - int inputs_count = params.inputs_count; - - int outer_size = 1; - for (int i = 0; i < axis; i++) - { - outer_size *= output_shape.Dims(i); - } - int copy_size = 1; - for (int i = params.axis + 1; i < dimensions; i++) - { - copy_size *= output_shape.Dims(i); - } - TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); - - for (int i = 0; i < inputs_count; ++i) - { - for (int k = 0; k < outer_size; k++) - { - const Scalar *input_ptr = input_data[i] + copy_size * k; - int loc = k * inputs_count * copy_size + i * copy_size; - memcpy(output_data + loc, input_ptr, copy_size * sizeof(Scalar)); - } - } -} - -template -void Unpack(const UnpackParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *const *output_datas) -{ - ruy::profiler::ScopeLabel label("Unpack"); - const int dimensions = input_shape.DimensionsCount(); - const int outputs_count = params.num_split; - - int outer_size = 1; - int axis = params.axis; - if (axis < 0) - { - axis += dimensions; - } - TFLITE_DCHECK_GE(axis, 0); - TFLITE_DCHECK_LT(axis, dimensions); - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - int copy_size = 1; - for (int i = axis + 1; i < dimensions; ++i) - { - copy_size *= input_shape.Dims(i); - } - TFLITE_DCHECK_EQ(output_shape.FlatSize(), copy_size * outer_size); - - for (int i = 0; i < outputs_count; ++i) - { - for (int k = 0; k < outer_size; k++) - { - Scalar *output_ptr = output_datas[i] + copy_size * k; - int loc = k * outputs_count * copy_size + i * copy_size; - memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); - } - } -} - -template -void PackWithScaling(const PackParams ¶ms, const RuntimeShape *const *input_shapes, - const uint8 *const *input_data, const RuntimeShape &output_shape, - uint8 *output_data) -{ - ruy::profiler::ScopeLabel label("PackWithScaling"); - const int dimensions = output_shape.DimensionsCount(); - int axis = params.axis; - const int32 *input_zeropoint = params.input_zeropoint; - const float *input_scale = params.input_scale; - int inputs_count = params.inputs_count; - const int32 output_zeropoint = params.output_zeropoint; - const float output_scale = params.output_scale; - - int outer_size = 1; - for (int i = 0; i < axis; i++) - { - outer_size *= output_shape.Dims(i); - } - int copy_size = 1; - for (int i = axis + 1; i < dimensions; i++) - { - copy_size *= output_shape.Dims(i); - } - TFLITE_DCHECK_EQ((**input_shapes).FlatSize(), copy_size * outer_size); - - Scalar *output_ptr = output_data; - const float inverse_output_scale = 1.f / output_scale; - for (int k = 0; k < outer_size; k++) - { - for (int i = 0; i < inputs_count; ++i) - { - if (input_zeropoint[i] == output_zeropoint && input_scale[i] == output_scale) - { - memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); - } - else - { - assert(false); - const float scale = input_scale[i] * inverse_output_scale; - const float bias = -input_zeropoint[i] * scale; - auto input_ptr = input_data[i]; - for (int j = 0; j < copy_size; ++j) - { - const int value = - static_cast(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; - output_ptr[j] = static_cast(std::max(std::min(255, value), 0)); - } - } - output_ptr += copy_size; - } - } -} - -template -void DepthConcatenation(const ConcatenationParams ¶ms, const RuntimeShape *const *input_shapes, - const Scalar *const *input_data, const RuntimeShape &output_shape, - Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("DepthConcatenation"); - auto params_copy = params; - params_copy.axis = 3; - Concatenation(params_copy, input_shapes, input_data, output_shape, output_data); -} - -inline void LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, - const float *input_data, const RuntimeShape &unextended_prev_activ_shape, - const float *prev_activ_data, const RuntimeShape &weights_shape, - const float *weights_data, const RuntimeShape &unextended_bias_shape, - const float *bias_data, const RuntimeShape &unextended_prev_state_shape, - const float *prev_state_data, - const RuntimeShape &unextended_output_state_shape, float *output_state_data, - const RuntimeShape &unextended_output_activ_shape, float *output_activ_data, - const RuntimeShape &unextended_concat_temp_shape, float *concat_temp_data, - const RuntimeShape &unextended_activ_temp_shape, float *activ_temp_data) -{ - TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); - const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); - const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); - const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); - const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); - const RuntimeShape output_state_shape = - RuntimeShape::ExtendedShape(4, unextended_output_state_shape); - const RuntimeShape output_activ_shape = - RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); - const RuntimeShape concat_temp_shape = - RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); - const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); - TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); - - const int weights_dim_count = weights_shape.DimensionsCount(); - const int batches = MatchingDim(input_shape, 0, prev_activ_shape, 0, prev_state_shape, 0, - output_state_shape, 0, output_activ_shape, 0); - const int height = MatchingDim(input_shape, 1, prev_activ_shape, 1, prev_state_shape, 1, - output_state_shape, 1, output_activ_shape, 1); - const int width = MatchingDim(input_shape, 2, prev_activ_shape, 2, prev_state_shape, 2, - output_state_shape, 2, output_activ_shape, 2); - const int input_depth = input_shape.Dims(3); - const int prev_activ_depth = prev_activ_shape.Dims(3); - const int total_input_depth = prev_activ_depth + input_depth; - TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); - TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); - const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); - TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); - TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); - const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, - 3, output_activ_shape, 3); - TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); - - // Concatenate prev_activ and input data together - std::vector concat_input_arrays_data; - std::vector concat_input_arrays_shapes; - concat_input_arrays_data.push_back(input_data); - concat_input_arrays_data.push_back(prev_activ_data); - concat_input_arrays_shapes.push_back(&input_shape); - concat_input_arrays_shapes.push_back(&prev_activ_shape); - tflite::ConcatenationParams concat_params; - concat_params.axis = 3; - concat_params.inputs_count = concat_input_arrays_data.size(); - Concatenation(concat_params, &(concat_input_arrays_shapes[0]), &(concat_input_arrays_data[0]), - concat_temp_shape, concat_temp_data); - - // Fully connected - tflite::FullyConnectedParams fc_params; - fc_params.float_activation_min = std::numeric_limits::lowest(); - fc_params.float_activation_max = std::numeric_limits::max(); - FullyConnected(fc_params, concat_temp_shape, concat_temp_data, weights_shape, weights_data, - bias_shape, bias_data, activ_temp_shape, activ_temp_data); - - // Memory state update (the LSTM "guts") - for (int b = 0; b < batches; ++b) - { - for (int w = 0; w < width; ++w) - { - for (int h = 0; h < height; ++h) - { - for (int c = 0; c < output_depth; ++c) - { - const float input_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 0 * output_depth + c)])); - const float new_input = - std::tanh(activ_temp_data[Offset(activ_temp_shape, b, h, w, 1 * output_depth + c)]); - const float forget_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 2 * output_depth + c)])); - const float output_gate = - 1.f / - (1.f + - std::exp(-activ_temp_data[Offset(activ_temp_shape, b, h, w, 3 * output_depth + c)])); - const float new_state = - input_gate * new_input + - forget_gate * prev_state_data[Offset(prev_state_shape, b, h, w, c)]; - output_state_data[Offset(output_state_shape, b, h, w, c)] = new_state; - output_activ_data[Offset(output_activ_shape, b, h, w, c)] = - output_gate * std::tanh(new_state); - } - } - } - } -} - -// Quantized LSTM cell implementation. -// The quantization of the input, output arrays is as follows: -// - The input activations are quantized as uint8 on the interval -// [-1, 127/128]. -// The rationale for that is that is the natural interval for output -// activations (see next point) and these need to be concatenated together. -// We could accommodate different ranges by re-scaling, but we empirically -// found that setting the input activations range to be [-1, 127/128] in the -// first place, removing the need for re-scaling, greatly improves accuracy. -// - The output activations are quantized as uint8 on the interval -// [-1, 127/128]. -// The rationale for that is that the definition of a LSTM cell makes them -// intrinsically constrained in [-1, 1]; tweaking that to [-1, 127/128] -// makes for simpler, more accurate fixed-point arithmetic. -// - The output-at-previous-timestep state array is obviously quantized as -// the output activations. -// - The internal LSTM memory (not the output-at-previous-timestep, the other -// internal state array) is int16-quantized and may use any power-of-two, -// symmetric range i.e. [-2^N, 2^N * 32767/32768] for any N, which we call -// StateIntegerBits below, see the below discussion of that template -// parameter ("The StateIntegerBits template parameter"). -// - The output of the internal fully-connected node is int16-quantized -// on the interval [-8, 8 * 32767/32768], the rationale for which is -// explained just below ("Why [-8, 8] for fully-connected output?"). -// -// -// === The StateIntegerBits template parameter === -// -// The StateIntegerBits template parameter controls the fixed-point format used -// to represent the internal memory of the LSTM cell (not the -// output-at-previous-timestep, the other internal state array). It's currently -// a template parameter so that the model can control that. The most typical -// value for StateIntegerBits is 4. Other plausible values are anywhere between -// 3 and 5. We might eventually standardize on a single supported value, e.g. 4, -// and drop that template parameter. The reason why it can't be a runtime -// parameter is that this controls the fixed-point format used, i.e. we need to -// generate actually different code based on it. In particular, we generate code -// for a fixed-point tanh() implementation for that format, which internally -// uses a fixed-point exp() implementation, which internally uses a -// barrel-shifter with a number of steps that depends on StateIntegerBits. -// Another consequence of that is that a higher value of StateIntegerBits -// results in a more expensive implementation (more barrel shifter steps -// needed). -// -// -// === Why [-8, 8] for fully-connected output? === -// -// This array is only fed to Logistic and Tanh functions, for which -// the quantized implementation will want to use fixed-point arithmetic, -// requiring a power-of-two representation interval. Thus, we should right -// away quantize this array to a power-of-two interval; otherwise, -// implementation will need to rescale that, losing any benefit that a tighter -// representation interval might otherwise yield, while introducing some -// numerical error and computational overhead. -// -// Now, Logistic and Tanh -// are nearly constant (nearly equal to their horizontal asymptotes) -// outside of a small bounded interval around 0: -// -// Logistic(4) = 1 - 1.8e-2 Tanh(4) = 1 - 6.7e-4 -// Logistic(8) = 1 - 3.4e-4 Tanh(8) = 1 - 2.3e-7 -// Logistic(16) = 1 - 1.1e-7 Tanh(16) = 1 - 2.5e-14 -// -// From this, we see that clamping to [-4, 4] would be too inaccurate -// (the error of 1.8e-2 on Logistic would be felt even in 8bit precision) -// while clamping to [-16, 16] would make no difference even in float32. -// However, for a fixed-point implementation in 16-bit integers, using 5 -// integer bits to represent the [-16, 16] range would leave only 11 -// fractional bits, giving an increment of 2^-11 = 4.9e-4 between consecutive -// representable values. Notice that is higher than the -// worst-case clamping error with clamping to [-8, 8]: 3.4e-4 for Logistic. -// Using [-8, 8] thus seems like the better compromise overall, enjoying -// an increment of 2.4e-4 between representable values and a worst-case -// clamping error of 3.4e-4, both better than the increment of 4.9e-4 with -// [-16, 16]. -// -// Moreover, all other things being equal, it is nice to choose the narrower -// representation range, as that makes the implementation of fixed-point -// math functions a little cheaper (each integer bit requires an additional -// barrel-shifter atep in the implementation of exp(-x)). That is further -// reason to prefer [-8, 8] over [-16, 16]. The choice of [-16, 16] would make -// sense for 32-bit float or 32-bit fixed-point quantization, but we are -// aiming for 16-bit fixed-point quantization of these internal nodes here. -// -template -inline void -LstmCell(const LstmCellParams ¶ms, const RuntimeShape &unextended_input_shape, - const uint8 *input_data_uint8, const RuntimeShape &unextended_prev_activ_shape, - const uint8 *prev_activ_data_uint8, const RuntimeShape &weights_shape, - const uint8 *weights_data_uint8, const RuntimeShape &unextended_bias_shape, - const int32 *bias_data_int32, const RuntimeShape &unextended_prev_state_shape, - const int16 *prev_state_data_int16, const RuntimeShape &unextended_output_state_shape, - int16 *output_state_data_int16, const RuntimeShape &unextended_output_activ_shape, - uint8 *output_activ_data_uint8, const RuntimeShape &unextended_concat_temp_shape, - uint8 *concat_temp_data_uint8, const RuntimeShape &unextended_activ_temp_shape, - int16 *activ_temp_data_int16, void *gemmlowp_context) -{ - (void)gemmlowp_context; // only used in optimized code. - int32 weights_zero_point = params.weights_zero_point; - int32 accum_multiplier = params.accum_multiplier; - int accum_shift = params.accum_shift; - TFLITE_DCHECK_LE(unextended_input_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_bias_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_prev_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_state_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_activ_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_concat_temp_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_activ_temp_shape.DimensionsCount(), 4); - const RuntimeShape input_shape = RuntimeShape::ExtendedShape(4, unextended_input_shape); - const RuntimeShape prev_activ_shape = RuntimeShape::ExtendedShape(4, unextended_prev_activ_shape); - const RuntimeShape bias_shape = RuntimeShape::ExtendedShape(4, unextended_bias_shape); - const RuntimeShape prev_state_shape = RuntimeShape::ExtendedShape(4, unextended_prev_state_shape); - const RuntimeShape output_state_shape = - RuntimeShape::ExtendedShape(4, unextended_output_state_shape); - const RuntimeShape output_activ_shape = - RuntimeShape::ExtendedShape(4, unextended_output_activ_shape); - const RuntimeShape concat_temp_shape = - RuntimeShape::ExtendedShape(4, unextended_concat_temp_shape); - const RuntimeShape activ_temp_shape = RuntimeShape::ExtendedShape(4, unextended_activ_temp_shape); - TFLITE_DCHECK_GE(weights_shape.DimensionsCount(), 2); - - // Gather dimensions information, and perform consistency checks. - const int weights_dim_count = weights_shape.DimensionsCount(); - const int outer_size = MatchingFlatSizeSkipDim(input_shape, 3, prev_activ_shape, prev_state_shape, - output_state_shape, output_activ_shape); - const int input_depth = input_shape.Dims(3); - const int prev_activ_depth = prev_activ_shape.Dims(3); - const int total_input_depth = prev_activ_depth + input_depth; - TFLITE_DCHECK_EQ(weights_shape.Dims(weights_dim_count - 1), total_input_depth); - const int intern_activ_depth = MatchingDim(weights_shape, weights_dim_count - 2, bias_shape, 3); - TFLITE_DCHECK_EQ(weights_shape.FlatSize(), intern_activ_depth * total_input_depth); - TFLITE_DCHECK_EQ(FlatSizeSkipDim(bias_shape, 3), 1); - TFLITE_DCHECK_EQ(intern_activ_depth % 4, 0); - const int output_depth = MatchingDim(prev_state_shape, 3, prev_activ_shape, 3, output_state_shape, - 3, output_activ_shape, 3); - TFLITE_DCHECK_EQ(output_depth, intern_activ_depth / 4); - const int fc_batches = FlatSizeSkipDim(activ_temp_shape, 3); - const int fc_output_depth = - MatchingDim(weights_shape, weights_dim_count - 2, activ_temp_shape, 3); - const int fc_accum_depth = total_input_depth; - TFLITE_DCHECK_EQ(fc_output_depth, 4 * output_depth); - - // Depth-concatenate prev_activ and input data together. - uint8 const *concat_input_arrays_data[2] = {input_data_uint8, prev_activ_data_uint8}; - const RuntimeShape *concat_input_arrays_shapes[2] = {&input_shape, &prev_activ_shape}; - tflite::ConcatenationParams concat_params; - concat_params.axis = 3; - concat_params.inputs_count = 2; - Concatenation(concat_params, concat_input_arrays_shapes, concat_input_arrays_data, - concat_temp_shape, concat_temp_data_uint8); - - // Implementation of the fully connected node inside the LSTM cell. - // The operands are 8-bit integers, the accumulators are internally 32bit - // integers, and the output is 16-bit fixed-point with 3 integer bits so - // the output range is [-2^3, 2^3] == [-8, 8]. The rationale for that - // is explained in the function comment above. - for (int b = 0; b < fc_batches; ++b) - { - for (int out_c = 0; out_c < fc_output_depth; ++out_c) - { - // Internal accumulation. - // Initialize accumulator with the bias-value. - int32 accum = bias_data_int32[out_c]; - // Accumulation loop. - for (int d = 0; d < fc_accum_depth; ++d) - { - int16 input_val = concat_temp_data_uint8[b * fc_accum_depth + d] - 128; - int16 weights_val = weights_data_uint8[out_c * fc_accum_depth + d] - weights_zero_point; - accum += input_val * weights_val; - } - // Down-scale the final int32 accumulator to the scale used by our - // (16-bit, using 3 integer bits) fixed-point format. The quantized - // multiplier and shift here have been pre-computed offline - // (e.g. by toco). - accum = MultiplyByQuantizedMultiplier(accum, accum_multiplier, accum_shift); - // Saturate, cast to int16, and store to the temporary activations array. - accum = std::max(-32768, std::min(32767, static_cast(accum))); - activ_temp_data_int16[out_c + fc_output_depth * b] = accum; - } - } - - // Rest of the LSTM cell: tanh and logistic math functions, and some adds - // and muls, all done in 16-bit fixed-point. - for (int b = 0; b < outer_size; ++b) - { - for (int c = 0; c < output_depth; ++c) - { - // Define the fixed-point data types that we will use here. All use - // int16 as the underlying integer type i.e. all are 16-bit fixed-point. - // They only differ by the number of integral vs. fractional bits, - // determining the range of values that they can represent. - // - // F0 uses 0 integer bits, range [-1, 1]. - // This is the return type of math functions such as tanh, logistic, - // whose range is in [-1, 1]. - using F0 = gemmlowp::FixedPoint; - // F3 uses 3 integer bits, range [-8, 8]. - // This is the range of the previous fully-connected node's output, - // which is our input here. - using F3 = gemmlowp::FixedPoint; - // FS uses StateIntegerBits integer bits, range [-2^StateIntegerBits, - // 2^StateIntegerBits]. It's used to represent the internal state, whose - // number of integer bits is currently dictated by the model. See comment - // on the StateIntegerBits template parameter above. - using FS = gemmlowp::FixedPoint; - // Implementation of input gate, using fixed-point logistic function. - F3 input_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 0 * output_depth + c]); - F0 input_gate_output = gemmlowp::logistic(input_gate_input); - // Implementation of input modulation gate, using fixed-point tanh - // function. - F3 input_modulation_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 1 * output_depth + c]); - F0 input_modulation_gate_output = gemmlowp::tanh(input_modulation_gate_input); - // Implementation of forget gate, using fixed-point logistic function. - F3 forget_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 2 * output_depth + c]); - F0 forget_gate_output = gemmlowp::logistic(forget_gate_input); - // Implementation of output gate, using fixed-point logistic function. - F3 output_gate_input = - F3::FromRaw(activ_temp_data_int16[b * fc_output_depth + 3 * output_depth + c]); - F0 output_gate_output = gemmlowp::logistic(output_gate_input); - // Implementation of internal multiplication nodes, still in fixed-point. - F0 input_times_input_modulation = input_gate_output * input_modulation_gate_output; - FS prev_state = FS::FromRaw(prev_state_data_int16[b * output_depth + c]); - FS prev_state_times_forget_state = forget_gate_output * prev_state; - // Implementation of internal addition node, saturating. - FS new_state = - gemmlowp::SaturatingAdd(gemmlowp::Rescale(input_times_input_modulation), - prev_state_times_forget_state); - // Implementation of last internal Tanh node, still in fixed-point. - // Since a Tanh fixed-point implementation is specialized for a given - // number or integer bits, and each specialization can have a substantial - // code size, and we already used above a Tanh on an input with 3 integer - // bits, and per the table in the above function comment there is no - // significant accuracy to be lost by clamping to [-8, +8] for a - // 3-integer-bits representation, let us just do that. This helps people - // porting this to targets where code footprint must be minimized. - F3 new_state_f3 = gemmlowp::Rescale<3>(new_state); - F0 output_activ_int16 = output_gate_output * gemmlowp::tanh(new_state_f3); - // Store the new internal state back to memory, as 16-bit integers. - // Note: here we store the original value with StateIntegerBits, not - // the rescaled 3-integer-bits value fed to tanh. - output_state_data_int16[b * output_depth + c] = new_state.raw(); - // Down-scale the output activations to 8-bit integers, saturating, - // and store back to memory. - int16 rescaled_output_activ = gemmlowp::RoundingDivideByPOT(output_activ_int16.raw(), 8); - int16 clamped_output_activ = - std::max(-128, std::min(127, rescaled_output_activ)); - output_activ_data_uint8[b * output_depth + c] = 128 + clamped_output_activ; - } - } -} - -template -void Split(const SplitParams ¶ms, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape *const *output_shapes, Scalar *const *output_data) -{ - ruy::profiler::ScopeLabel label("Split"); - const int split_dimensions = input_shape.DimensionsCount(); - int axis = params.axis < 0 ? params.axis + split_dimensions : params.axis; - int outputs_count = params.num_split; - TFLITE_DCHECK_LT(axis, split_dimensions); - - int64_t split_size = 0; - for (int i = 0; i < outputs_count; i++) - { - TFLITE_DCHECK_EQ(output_shapes[i]->DimensionsCount(), split_dimensions); - for (int j = 0; j < split_dimensions; j++) - { - if (j != axis) - { - MatchingDim(*output_shapes[i], j, input_shape, j); - } - } - split_size += output_shapes[i]->Dims(axis); - } - TFLITE_DCHECK_EQ(split_size, input_shape.Dims(axis)); - int64_t outer_size = 1; - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - // For all output arrays, - // FlatSize() = outer_size * Dims(axis) * base_inner_size; - int64_t base_inner_size = 1; - for (int i = axis + 1; i < split_dimensions; ++i) - { - base_inner_size *= input_shape.Dims(i); - } - - const Scalar *input_ptr = input_data; - for (int k = 0; k < outer_size; k++) - { - for (int i = 0; i < outputs_count; ++i) - { - const int copy_size = output_shapes[i]->Dims(axis) * base_inner_size; - memcpy(output_data[i] + k * copy_size, input_ptr, copy_size * sizeof(Scalar)); - input_ptr += copy_size; - } - } -} - -inline int NodeOffset(int b, int h, int w, int height, int width) -{ - return (b * height + h) * width + w; -} - -inline void LocalResponseNormalization(const tflite::LocalResponseNormalizationParams &op_params, - const RuntimeShape &input_shape, const float *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - const int trailing_dim = input_shape.DimensionsCount() - 1; - const int outer_size = MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape); - const int depth = MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim); - - for (int i = 0; i < outer_size; ++i) - { - for (int c = 0; c < depth; ++c) - { - const int begin_input_c = std::max(0, static_cast(c - op_params.range)); - const int end_input_c = std::min(depth, static_cast(c + op_params.range)); - float accum = 0.f; - for (int input_c = begin_input_c; input_c < end_input_c; ++input_c) - { - const float input_val = input_data[i * depth + input_c]; - accum += input_val * input_val; - } - const float multiplier = std::pow(op_params.bias + op_params.alpha * accum, -op_params.beta); - output_data[i * depth + c] = input_data[i * depth + c] * multiplier; - } - } -} - -inline void Dequantize(const RuntimeShape &input_shape, const Eigen::half *input_data, - const RuntimeShape &output_shape, float *output_data) -{ - const int flat_size = MatchingFlatSize(input_shape, output_shape); - for (int i = 0; i < flat_size; i++) - { - output_data[i] = static_cast(input_data[i]); - } -} - -inline void FakeQuant(const tflite::FakeQuantParams &op_params, const RuntimeShape &input_shape, - const float *input_data, const RuntimeShape &output_shape, float *output_data) -{ - ruy::profiler::ScopeLabel label("FakeQuant"); - float rmin = op_params.minmax.min; - float rmax = op_params.minmax.max; - int num_bits = op_params.num_bits; - // 0 should always be a representable value. Let's assume that the initial - // min,max range contains 0. - TFLITE_DCHECK_LE(rmin, 0.0f); - TFLITE_DCHECK_GE(rmax, 0.0f); - TFLITE_DCHECK_LT(rmin, rmax); - - // Code matches tensorflow's FakeQuantWithMinMaxArgsFunctor. - int quant_min = 0; - int quant_max = (1 << num_bits) - 1; - float nudged_min, nudged_max, nudged_scale; - NudgeQuantizationRange(rmin, rmax, quant_min, quant_max, &nudged_min, &nudged_max, &nudged_scale); - const int flat_size = MatchingFlatSize(input_shape, output_shape); - FakeQuantizeArray(nudged_scale, nudged_min, nudged_max, input_data, output_data, flat_size); -} - -// Common subroutine for both `GatherNd` and `GatherNdString`. -struct GatherNdHelperResult -{ - int n_slices; - int slice_size; - int indices_nd; - std::vector dims_to_count; -}; - -// Returns common values being used on both `GatherNd` and `GatherNdString`. -inline GatherNdHelperResult GatherNdHelper(const RuntimeShape ¶ms_shape, - const RuntimeShape &indices_shape) -{ - GatherNdHelperResult ret; - ret.n_slices = 1; - ret.slice_size = 1; - const int indices_dims = indices_shape.DimensionsCount(); - ret.indices_nd = indices_shape.Dims(indices_dims - 1); - const int params_dims = params_shape.DimensionsCount(); - for (int i = 0; i < indices_dims - 1; ++i) - { - ret.n_slices *= indices_shape.Dims(i); - } - for (int i = ret.indices_nd; i < params_dims; ++i) - { - ret.slice_size *= params_shape.Dims(i); - } - - int remain_flat_size = params_shape.FlatSize(); - ret.dims_to_count = std::vector(ret.indices_nd, 0); - for (int i = 0; i < ret.indices_nd; ++i) - { - ret.dims_to_count[i] = remain_flat_size / params_shape.Dims(i); - remain_flat_size = ret.dims_to_count[i]; - } - - return ret; -} - -template -inline void GatherNd(const RuntimeShape ¶ms_shape, const ParamsT *params_data, - const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &output_shape, ParamsT *output_data) -{ - ruy::profiler::ScopeLabel label("GatherNd"); - - const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); - for (int i = 0; i < res.n_slices; ++i) - { - int from_pos = 0; - for (int j = 0; j < res.indices_nd; ++j) - { - from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; - } - std::memcpy(output_data + i * res.slice_size, params_data + from_pos, - sizeof(ParamsT) * res.slice_size); - } -} - -#ifndef TF_LITE_STATIC_MEMORY -template -inline void GatherNdString(const RuntimeShape ¶ms_shape, const TfLiteTensor *params_data, - const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &output_shape, TfLiteTensor *output_data) -{ - ruy::profiler::ScopeLabel label("GatherNdString"); - - const GatherNdHelperResult res = GatherNdHelper(params_shape, indices_shape); - DynamicBuffer buffer; - for (int i = 0; i < res.n_slices; ++i) - { - int from_pos = 0; - for (int j = 0; j < res.indices_nd; ++j) - { - from_pos += indices_data[i * res.indices_nd + j] * res.dims_to_count[j]; - } - for (int j = 0; j < res.slice_size; ++j) - { - buffer.AddString(GetString(params_data, from_pos + j)); - } - } - buffer.WriteToTensor(output_data, /*new_shape=*/nullptr); -} -#endif - -template -inline void ScatterNd(const RuntimeShape &indices_shape, const IndicesT *indices_data, - const RuntimeShape &updates_shape, const UpdatesT *updates_data, - const RuntimeShape &output_shape, UpdatesT *output_data) -{ - ruy::profiler::ScopeLabel label("ScatterNd"); - - int n_slices = 1; - int slice_size = 1; - const int outer_dims = indices_shape.DimensionsCount() - 1; - const int indices_nd = indices_shape.Dims(outer_dims); - const int updates_dims = updates_shape.DimensionsCount(); - for (int i = 0; i < outer_dims; ++i) - { - n_slices *= indices_shape.Dims(i); - } - for (int i = outer_dims; i < updates_dims; ++i) - { - slice_size *= updates_shape.Dims(i); - } - - int output_flat_size = output_shape.FlatSize(); - int remain_flat_size = output_flat_size; - std::vector dims_to_count(indices_nd, 0); - for (int i = 0; i < indices_nd; ++i) - { - dims_to_count[i] = remain_flat_size / output_shape.Dims(i); - remain_flat_size = dims_to_count[i]; - } - - memset(output_data, 0, sizeof(UpdatesT) * output_flat_size); - for (int i = 0; i < n_slices; ++i) - { - int to_pos = 0; - for (int j = 0; j < indices_nd; ++j) - { - IndicesT idx = indices_data[i * indices_nd + j]; - TFLITE_DCHECK(0 <= idx && idx < output_shape.Dims(j)); - to_pos += idx * dims_to_count[j]; - } - for (int j = 0; j < slice_size; j++) - { - output_data[to_pos + j] += updates_data[i * slice_size + j]; - } - } -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const RuntimeShape &output_shape, SequentialTensorWriter *writer) -{ - const RuntimeShape ext_shape = RuntimeShape::ExtendedShape(5, input_shape); - TFLITE_DCHECK_LE(op_params.begin_count, 5); - TFLITE_DCHECK_LE(op_params.size_count, 5); - const int begin_count = op_params.begin_count; - const int size_count = op_params.size_count; - // We front-pad the begin and size vectors. - std::array start; - std::array stop; - for (int i = 0; i < 5; ++i) - { - int padded_i = 5 - i; - start[i] = begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; - stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) - ? ext_shape.Dims(i) - : start[i] + op_params.size[size_count - padded_i]; - } - - for (int i0 = start[0]; i0 < stop[0]; ++i0) - { - for (int i1 = start[1]; i1 < stop[1]; ++i1) - { - for (int i2 = start[2]; i2 < stop[2]; ++i2) - { - for (int i3 = start[3]; i3 < stop[3]; ++i3) - { - for (int i4 = start[4]; i4 < stop[4]; ++i4) - { - writer->Write(Offset(ext_shape, i0, i1, i2, i3, i4)); - } - } - } - } - } -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const T *input_data, const RuntimeShape &output_shape, T *output_data) -{ - SequentialTensorWriter writer(input_data, output_data); - return Slice(op_params, input_shape, output_shape, &writer); -} - -template -inline void Slice(const tflite::SliceParams &op_params, const RuntimeShape &input_shape, - const TfLiteTensor *input, const RuntimeShape &output_shape, TfLiteTensor *output) -{ - SequentialTensorWriter writer(input, output); - return Slice(op_params, input_shape, output_shape, &writer); -} - -template -void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, output_shape); - - auto min_value = input2_data[0]; - for (int i = 0; i < flat_size; i++) - { - output_data[i] = input1_data[i] > min_value ? min_value : input1_data[i]; - } -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void Minimum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, - const T *input2_data, const RuntimeShape &output_shape, T *output_data) -{ - // Drop shape of second input: not needed. - Minimum(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, output_shape); - - auto max_value = input2_data[0]; - for (int i = 0; i < flat_size; i++) - { - output_data[i] = input1_data[i] < max_value ? max_value : input1_data[i]; - } -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void Maximum(const RuntimeShape &input1_shape, const T *input1_data, const RuntimeShape &, - const T *input2_data, const RuntimeShape &output_shape, T *output_data) -{ - // Drop shape of second input: not needed. - Maximum(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, const T3 *input2_data, - const RuntimeShape &output_shape, T2 *output_data) -{ - ArgMinMax(input1_shape, input1_data, input2_data, output_shape, output_data, std::greater()); -} - -// Convenience version that allows, for example, generated-code calls to be -// the same as other binary ops. -template -inline void ArgMax(const RuntimeShape &input1_shape, const T1 *input1_data, - const RuntimeShape &input2_shape, const T3 *input2_data, - const RuntimeShape &output_shape, T2 *output_data) -{ - // Drop shape of second input: not needed. - ArgMax(input1_shape, input1_data, input2_data, output_shape, output_data); -} - -template -void Select(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - int64_t flatsize; - // Allow select operator executions on mixed scalar tensors and one element - // tensors. - if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 && - input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) - { - flatsize = 1; - } - else - { - flatsize = MatchingFlatSize(input_condition_shape, input_x_shape, input_y_shape, output_shape); - } - for (int64_t i = 0; i < flatsize; ++i) - { - output_data[i] = input_condition_data[i] ? input_x_data[i] : input_y_data[i]; - } -} - -template -void RankOneSelect(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int64_t outer_size = input_condition_shape.FlatSize(); - int64_t inner_size; - if (input_condition_shape.DimensionsCount() == 0) - { - inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape); - } - else - { - TFLITE_DCHECK_EQ(MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0), outer_size); - inner_size = MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape); - } - - int64_t offset = 0; - for (int64_t i = 0; i < outer_size; i++) - { - const T *input_data = input_condition_data[i] ? input_x_data : input_y_data; - memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T)); - offset += inner_size; - } -} - -template -void BroadcastSelect4DSlow(const RuntimeShape &input_condition_shape, const D *input_condition_data, - const RuntimeShape &input_x_shape, const T *input_x_data, - const RuntimeShape &input_y_shape, const T *input_y_data, - const RuntimeShape &output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4); - - const RuntimeShape extended_output_shape = RuntimeShape::ExtendedShape(4, output_shape); - - NdArrayDesc<4> desc_condition; - NdArrayDesc<4> desc_x; - NdArrayDesc<4> desc_y; - NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape, input_y_shape, - &desc_condition, &desc_x, &desc_y); - - // In Tensorflow, the dimensions are canonically named (batch_number, row, - // col, channel), with extents (batches, height, width, depth), with the - // trailing dimension changing most rapidly (channels has the smallest - // stride, typically 1 element). - // - // In generated C code, we store arrays with the dimensions reversed. The - // first dimension has smallest stride. - // - // We name our variables by their Tensorflow convention, but generate C code - // nesting loops such that the innermost loop has the smallest stride for - // the best cache behavior. - for (int b = 0; b < extended_output_shape.Dims(0); ++b) - { - for (int y = 0; y < extended_output_shape.Dims(1); ++y) - { - for (int x = 0; x < extended_output_shape.Dims(2); ++x) - { - for (int c = 0; c < extended_output_shape.Dims(3); ++c) - { - const int condition_index = SubscriptToIndex(desc_condition, b, y, x, c); - const int x_index = SubscriptToIndex(desc_x, b, y, x, c); - const int y_index = SubscriptToIndex(desc_y, b, y, x, c); - output_data[Offset(extended_output_shape, b, y, x, c)] = - input_condition_data[condition_index] ? input_x_data[x_index] : input_y_data[y_index]; - } - } - } - } -} - -template -void SelectTrueCoords(const RuntimeShape &input_condition_shape, const D *input_condition_data, - T *output_data) -{ - const size_t size = input_condition_shape.FlatSize(); - if (size == 0) - { - // Dimension is zero, in which case we don't need to output. - return; - } - const size_t cond_rank = input_condition_shape.DimensionsCount(); - - std::vector dims_to_count(cond_rank, 0); - int cur_flat_size = size; - for (int i = 0; i < cond_rank; ++i) - { - dims_to_count[i] = cur_flat_size / input_condition_shape.Dims(i); - cur_flat_size = dims_to_count[i]; - } - - int output_index = 0; - for (int i = 0; i < size; ++i) - { - if (input_condition_data[i]) - { - // Insert the coordinate of the current item (row major) into output. - int flat_index = i; - for (int j = 0; j < cond_rank; ++j) - { - int coord_j = flat_index / dims_to_count[j]; - output_data[output_index * cond_rank + j] = coord_j; - flat_index %= dims_to_count[j]; - } - output_index++; - } - } -} - -// For easy implementation, the indices is always a vector of size-4 vectors. -template -inline void SparseToDense(const std::vector> &indices, const T *values, - T default_value, bool value_is_scalar, - const RuntimeShape &unextended_output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); - const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); - const int value_count = indices.size(); - - // First fill the output_data with default value. - const int num_elements = output_shape.FlatSize(); - for (int i = 0; i < num_elements; ++i) - { - output_data[i] = default_value; - } - - // Special handle for value is scalar case to avoid checking the boolean - // condition within the loop every time. - if (value_is_scalar) - { - for (int i = 0; i < value_count; ++i) - { - const std::vector &index = indices[i]; - TFLITE_DCHECK_EQ(index.size(), 4); - const T value = *values; // just use the first value. - output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; - } - return; - } - - // Go through the values and indices to fill the sparse values. - for (int i = 0; i < value_count; ++i) - { - const std::vector &index = indices[i]; - TFLITE_DCHECK_EQ(index.size(), 4); - const T value = values[i]; - output_data[Offset(output_shape, index[0], index[1], index[2], index[3])] = value; - } -} - -template -inline void Pow(const RuntimeShape &input1_shape, const T *input1_data, - const RuntimeShape &input2_shape, const T *input2_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int flat_size = MatchingFlatSize(input1_shape, input2_shape, output_shape); - for (int i = 0; i < flat_size; ++i) - { - output_data[i] = std::pow(input1_data[i], input2_data[i]); - } -} - -template -inline void BroadcastPow4DSlow(const RuntimeShape &unextended_input1_shape, const T *input1_data, - const RuntimeShape &unextended_input2_shape, const T *input2_data, - const RuntimeShape &unextended_output_shape, T *output_data) -{ - TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4); - TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4); - const RuntimeShape output_shape = RuntimeShape::ExtendedShape(4, unextended_output_shape); - - NdArrayDesc<4> desc1; - NdArrayDesc<4> desc2; - NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, &desc1, - &desc2); - - for (int b = 0; b < output_shape.Dims(0); ++b) - { - for (int y = 0; y < output_shape.Dims(1); ++y) - { - for (int x = 0; x < output_shape.Dims(2); ++x) - { - for (int c = 0; c < output_shape.Dims(3); ++c) - { - auto out_idx = Offset(output_shape, b, y, x, c); - auto in1_idx = SubscriptToIndex(desc1, b, y, x, c); - auto in2_idx = SubscriptToIndex(desc2, b, y, x, c); - auto in1_val = input1_data[in1_idx]; - auto in2_val = input2_data[in2_idx]; - output_data[out_idx] = std::pow(in1_val, in2_val); - } - } - } - } -} - -template -void Reverse(int axis, const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("Reverse"); - - int outer_size = 1; - for (int i = 0; i < axis; ++i) - { - outer_size *= input_shape.Dims(i); - } - - int copy_size = 1; - for (int i = axis + 1; i < input_shape.DimensionsCount(); ++i) - { - copy_size *= input_shape.Dims(i); - } - - const int dims_at_axis = input_shape.Dims(axis); - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_axis; ++j) - { - const int start_pos = (i * dims_at_axis + j) * copy_size; - Scalar *output_ptr = output_data + start_pos; - int loc = (i * dims_at_axis + dims_at_axis - j - 1) * copy_size; - memcpy(output_ptr, input_data + loc, copy_size * sizeof(Scalar)); - } - } -} - -template -void ReverseSequence(const TS *seq_lengths, const int seq_dim, const int batch_dim, - const RuntimeShape &input_shape, const Scalar *input_data, - const RuntimeShape &output_shape, Scalar *output_data) -{ - ruy::profiler::ScopeLabel label("ReverseSequence"); - - int outer_size = 1; - int outer_dim = std::min(batch_dim, seq_dim); - int medium_dim = std::max(batch_dim, seq_dim); - for (int i = 0; i < outer_dim; ++i) - { - outer_size *= input_shape.Dims(i); - } - - int medium_size = 1; - for (int i = outer_dim + 1; i < medium_dim; ++i) - { - medium_size *= input_shape.Dims(i); - } - - int copy_size = 1; - for (int i = medium_dim + 1; i < input_shape.DimensionsCount(); ++i) - { - copy_size *= input_shape.Dims(i); - } - - const int dims_at_outer_dim = input_shape.Dims(outer_dim); - const int dims_at_medium_dim = input_shape.Dims(medium_dim); - - Scalar *output_ptr; - if (batch_dim > seq_dim) - { - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_outer_dim; ++j) - { - const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; - for (int p = 0; p < medium_size; ++p) - { - for (int q = 0; q < dims_at_medium_dim; ++q) - { - const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; - const Scalar *in_ptr = input_data + in_pos; - int sl = seq_lengths[q] - 1; - if (j > sl) - { - output_ptr = output_data + in_pos; - } - else - { - const int out_pos_base = (i * dims_at_outer_dim + sl - j) * medium_size; - const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + q) * copy_size; - output_ptr = output_data + out_pos; - } - memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); - } - } - } - } - } - else if (batch_dim < seq_dim) - { - for (int i = 0; i < outer_size; ++i) - { - for (int j = 0; j < dims_at_outer_dim; ++j) - { - const int in_pos_base = (i * dims_at_outer_dim + j) * medium_size; - int sl = seq_lengths[j] - 1; - const int out_pos_base = (i * dims_at_outer_dim + j) * medium_size; - for (int p = 0; p < medium_size; ++p) - { - for (int q = 0; q < dims_at_medium_dim; ++q) - { - const int in_pos = ((in_pos_base + p) * dims_at_medium_dim + q) * copy_size; - const Scalar *in_ptr = input_data + in_pos; - if (q > sl) - { - output_ptr = output_data + in_pos; - } - else - { - const int out_pos = ((out_pos_base + p) * dims_at_medium_dim + sl - q) * copy_size; - output_ptr = output_data + out_pos; - } - memcpy(output_ptr, in_ptr, copy_size * sizeof(Scalar)); - } - } - } - } - } -} - -template -inline void SegmentSum(const RuntimeShape &input_shape, const T *input_data, - const RuntimeShape &segment_ids_shape, const int32_t *segment_ids_data, - const RuntimeShape &output_shape, T *output_data) -{ - const int segment_flat_size = MatchingFlatSizeSkipDim(input_shape, 0, output_shape); - - memset(output_data, 0, sizeof(T) * output_shape.FlatSize()); - - for (int i = 0; i < input_shape.Dims(0); i++) - { - int output_index = segment_ids_data[i]; - for (int j = 0; j < segment_flat_size; ++j) - { - output_data[output_index * segment_flat_size + j] += input_data[i * segment_flat_size + j]; - } - } -} - -} // namespace reference_ops -} // namespace tflite - -#endif // LUCI_INTERPRETER_PAL_REFERENCE_OPS_H diff --git a/onert-micro/luci-interpreter/pal/mcu/pal.cmake b/onert-micro/luci-interpreter/pal/mcu/pal.cmake index 24e469f..fe528ad 100644 --- a/onert-micro/luci-interpreter/pal/mcu/pal.cmake +++ b/onert-micro/luci-interpreter/pal/mcu/pal.cmake @@ -1,56 +1,7 @@ macro(initialize_pal) - nnas_find_package(TensorFlowSource EXACT 2.8.0 REQUIRED) - nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.8.0 REQUIRED) - nnas_find_package(TensorFlowEigenSource EXACT 2.8.0 REQUIRED) - nnas_find_package(TensorFlowRuySource EXACT 2.8.0 REQUIRED) - - if (NOT TensorFlowSource_FOUND) - message(STATUS "Skipping luci-interpreter: TensorFlow not found") - return() - endif () - - if (NOT TensorFlowGEMMLowpSource_FOUND) - message(STATUS "Skipping luci-interpreter: gemmlowp not found") - return() - endif () - - if (NOT TensorFlowEigenSource_FOUND) - message(STATUS "Skipping luci-interpreter: Eigen not found") - return() - endif () - - if (NOT TensorFlowRuySource_FOUND) - message(STATUS "Skipping luci-interpreter: Ruy not found") - return() - endif () - #find_package(Threads REQUIRED) - set(PAL_INITIALIZED TRUE) endmacro() macro(add_pal_to_target TGT) - target_include_directories(${TGT} PRIVATE "${PAL}") - target_include_directories(${TGT} PRIVATE - "${TensorFlowRuySource_DIR}" - "${TensorFlowGEMMLowpSource_DIR}" - "${TensorFlowEigenSource_DIR}" - "${TensorFlowSource_DIR}") - target_include_directories(${TGT} PRIVATE ${LUCI_INTERPRETER_PAL_DIR}) - - # TODO put it back, I changed my mind. - # instead add sources with visitors in this library - set(PAL_SOURCES ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/tensor_utils.cc - ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/reference/portable_tensor_utils.cc) - add_library(luci_interpreter_mcu_pal STATIC ${PAL_SOURCES}) - set_target_properties(luci_interpreter_mcu_pal PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_include_directories(luci_interpreter_mcu_pal PRIVATE - "${TensorFlowRuySource_DIR}" - "${TensorFlowGEMMLowpSource_DIR}" - "${TensorFlowEigenSource_DIR}" - "${TensorFlowSource_DIR}" - ) - - target_link_libraries(${TGT} PRIVATE luci_interpreter_mcu_pal) - #target_link_libraries(${TGT} PRIVATE Threads::Threads luci_interpreter_mcu_pal) + target_include_directories(${TGT} PUBLIC ${LUCI_INTERPRETER_PAL_DIR}) endmacro() diff --git a/onert-micro/luci-interpreter/src/Interpreter.cpp b/onert-micro/luci-interpreter/src/Interpreter.cpp index dccef82..658b28f 100644 --- a/onert-micro/luci-interpreter/src/Interpreter.cpp +++ b/onert-micro/luci-interpreter/src/Interpreter.cpp @@ -52,9 +52,9 @@ Interpreter::Interpreter(const char *model_data_raw, const InterpreterConfigure #else // Construct default interpreter with dynamic allocations and with input allocations -Interpreter::Interpreter(const char *model_data_raw) +Interpreter::Interpreter(const char *model_data_raw, bool dealloc_input) { - ModuleLoader::load(&_runtime_module, &_memory_manager, model_data_raw); + ModuleLoader::load(&_runtime_module, &_memory_manager, model_data_raw, dealloc_input); } #endif // USE_STATIC_ALLOC diff --git a/onert-micro/luci-interpreter/src/core/RuntimeGraph.cpp b/onert-micro/luci-interpreter/src/core/RuntimeGraph.cpp index 41447f9..917ebbb 100644 --- a/onert-micro/luci-interpreter/src/core/RuntimeGraph.cpp +++ b/onert-micro/luci-interpreter/src/core/RuntimeGraph.cpp @@ -24,10 +24,13 @@ namespace luci_interpreter { // IBaseRuntimeGraph -RuntimeGraph::RuntimeGraph(SimpleMemoryManager *memory_manager, CircleReader *circle_reader) +RuntimeGraph::RuntimeGraph(SimpleMemoryManager *memory_manager, CircleReader *circle_reader, + RuntimeModule *runtime_module, uint32_t subgraph_index) : _memory_manager(memory_manager), _tensor_to_data(std::unordered_map{}), - _reader(circle_reader), _inplace_op_indexes(std::unordered_set{}) + _runtime_module(runtime_module), _reader(circle_reader), + _inplace_op_indexes(std::unordered_set{}), + _subgraph_index(subgraph_index) { } @@ -42,19 +45,23 @@ RuntimeGraph::~RuntimeGraph() } // TODO: modify this -void RuntimeGraph::buildAllocDeallocPlan() +void RuntimeGraph::buildAllocDeallocPlan(bool dealloc_input) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); invalidate(); using Lifetime = std::pair; std::map lifetimes; const size_t num_kernels = _reader->operators().size(); - for (const auto input_ind : _reader->inputs()) + if (dealloc_input) { - const auto raw_tensor = _reader->tensors()[input_ind]; + for (const auto input_ind : _reader->inputs()) + { + const auto raw_tensor = _reader->tensors()[input_ind]; - assert(lifetimes.count(raw_tensor) == 0); - lifetimes[raw_tensor] = Lifetime(-1, 0); + assert(lifetimes.count(raw_tensor) == 0); + lifetimes[raw_tensor] = Lifetime(-1, 0); + } } for (int32_t index = 0; index < num_kernels; ++index) @@ -72,16 +79,12 @@ void RuntimeGraph::buildAllocDeallocPlan() const auto raw_tensor = _reader->tensors()[input_index]; // Pass constant tensors - auto const &buffer = wrap(_reader->buffers()[raw_tensor->buffer()]->data()); - if (not buffer.empty()) - { - // unknown shape tensor and scalar tensor + if (Tensor::is_constant_tensor(_reader, raw_tensor)) continue; - } if (lifetimes.count(raw_tensor) > 0) { - if (_inplace_op_indexes.find(index) != _inplace_op_indexes.end()) + if (_inplace_op_indexes.find(kernel) != _inplace_op_indexes.end()) lifetimes.at(raw_tensor).second = -1; else lifetimes.at(raw_tensor).second = index; @@ -94,7 +97,7 @@ void RuntimeGraph::buildAllocDeallocPlan() const auto raw_tensor = _reader->tensors()[output_index]; assert(lifetimes.count(raw_tensor) == 0); - if (_inplace_op_indexes.find(index) != _inplace_op_indexes.end()) + if (_inplace_op_indexes.find(kernel) != _inplace_op_indexes.end()) lifetimes[raw_tensor] = Lifetime(-1, index); else lifetimes[raw_tensor] = Lifetime(index, index); @@ -123,6 +126,7 @@ void RuntimeGraph::buildAllocDeallocPlan() void RuntimeGraph::allocate(size_t kernel_index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); assert(_is_valid && kernel_index < _alloc_plan.size()); for (const circle::Tensor *tensor : _alloc_plan[kernel_index]) { @@ -136,8 +140,37 @@ void RuntimeGraph::allocate(size_t kernel_index) } } +#ifndef DIS_DYN_SHAPES +void RuntimeGraph::addDynamicShapeTensor(const circle::Tensor *tensor, + luci_interpreter::RuntimeShape &&shapes) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + _dynamic_tensor_shapes[tensor] = std::move(shapes); +} + +luci_interpreter::RuntimeShape *RuntimeGraph::getDynamicShapeTensor(const circle::Tensor *tensor) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + auto it = _dynamic_tensor_shapes.find(tensor); + + return it == _dynamic_tensor_shapes.end() ? nullptr : &_dynamic_tensor_shapes[tensor]; +} + +void RuntimeGraph::removeDynamicShapeTensor(const circle::Tensor *tensor) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + auto it = _dynamic_tensor_shapes.find(tensor); + + assert(it != _dynamic_tensor_shapes.end()); + + _dynamic_tensor_shapes.erase(it); +} + +#endif // DIS_DYN_SHAPES + void RuntimeGraph::deallocate(size_t kernel_index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); assert(_is_valid && kernel_index < _dealloc_plan.size()); for (const circle::Tensor *tensor : _dealloc_plan[kernel_index]) { @@ -151,12 +184,31 @@ void RuntimeGraph::deallocate(size_t kernel_index) } } +void RuntimeGraph::resetTensorData(uint8_t *new_data, const circle::Tensor *tensor) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + auto tensor_it = _tensor_to_data.find(tensor); + if (tensor_it != _tensor_to_data.end()) + { + auto *data = _tensor_to_data.at(tensor); + _memory_manager->release_memory(data); + } + + _tensor_to_data[tensor] = new_data; +} + void RuntimeGraph::resetOutputTensorsData() { + assert(_reader->get_current_subgraph_index() == _subgraph_index); + const auto graph_inputs = _reader->inputs(); for (int i = 0; i < _reader->outputs().size(); ++i) { const auto tensor_index = _reader->outputs()[i]; assert(tensor_index != -1); + + if (std::find(graph_inputs.begin(), graph_inputs.end(), tensor_index) != graph_inputs.end()) + return; + const auto tensor = _reader->tensors()[tensor_index]; assert(tensor != nullptr); @@ -172,29 +224,22 @@ void RuntimeGraph::resetOutputTensorsData() uint8_t *RuntimeGraph::configureGraphInput(int32_t input_index) { - resetOutputTensorsData(); + assert(_reader->get_current_subgraph_index() == _subgraph_index); const auto tensor_index = _reader->inputs()[input_index]; assert(tensor_index != -1); const auto tensor = _reader->tensors()[tensor_index]; assert(tensor != nullptr); - if (_tensor_to_data.find(tensor) != _tensor_to_data.end()) - { - auto *data = _tensor_to_data.at(tensor); - _memory_manager->release_memory(data); - } - auto *data = _memory_manager->allocate_memory(tensor); - _tensor_to_data[tensor] = data; + configureGraphInput(input_index, data); return data; } -// To save data -// TODO maybe remove it void RuntimeGraph::configureGraphInput(int32_t input_index, uint8_t *data) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); resetOutputTensorsData(); const auto tensor_index = _reader->inputs()[input_index]; @@ -205,13 +250,15 @@ void RuntimeGraph::configureGraphInput(int32_t input_index, uint8_t *data) if (_tensor_to_data.find(tensor) != _tensor_to_data.end()) { auto *data_prev = _tensor_to_data.at(tensor); - _memory_manager->release_memory(data_prev); + if (data_prev != data) + _memory_manager->release_memory(data_prev); } _tensor_to_data[tensor] = data; } int32_t RuntimeGraph::getInputDataSizeByIndex(int32_t input_index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); const auto tensor_index = _reader->inputs()[input_index]; assert(tensor_index != -1); const auto tensor = _reader->tensors()[tensor_index]; @@ -220,8 +267,42 @@ int32_t RuntimeGraph::getInputDataSizeByIndex(int32_t input_index) return Tensor::num_elements(tensor) * size(Tensor::element_type(tensor)); } +int32_t RuntimeGraph::getNumOfInputTensors() +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + return _reader->inputs().size(); +} + +int32_t RuntimeGraph::getNumOfOutputTensors() +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + return _reader->outputs().size(); +} + +const circle::Tensor *RuntimeGraph::getInputTensorByIndex(int32_t input_index) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + + const auto tensor_index = _reader->inputs()[input_index]; + const auto tensor = _reader->tensors()[tensor_index]; + assert(tensor != nullptr); + return tensor; +} + +const circle::Tensor *RuntimeGraph::getOutputTensorByIndex(int32_t input_index) +{ + assert(_reader->get_current_subgraph_index() == _subgraph_index); + + const auto tensor_index = _reader->outputs()[input_index]; + const auto tensor = _reader->tensors()[tensor_index]; + assert(tensor != nullptr); + return tensor; +} + int32_t RuntimeGraph::getOutputDataSizeByIndex(int32_t output_index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); + const auto tensor_index = _reader->outputs()[output_index]; assert(tensor_index != -1); const auto tensor = _reader->tensors()[tensor_index]; @@ -232,6 +313,8 @@ int32_t RuntimeGraph::getOutputDataSizeByIndex(int32_t output_index) uint8_t *RuntimeGraph::getOutputDataByIndex(int32_t output_index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); + const auto tensor_index = _reader->outputs()[output_index]; assert(tensor_index != -1); const auto tensor = _reader->tensors()[tensor_index]; @@ -244,6 +327,8 @@ uint8_t *RuntimeGraph::getOutputDataByIndex(int32_t output_index) uint8_t *RuntimeGraph::getDataByTensor(const circle::Tensor *raw_tensor) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); + if (raw_tensor == nullptr) return nullptr; @@ -255,20 +340,29 @@ uint8_t *RuntimeGraph::getDataByTensor(const circle::Tensor *raw_tensor) return _tensor_to_data.at(raw_tensor); } -void RuntimeGraph::makeInplaceOperation(const circle::Tensor *src_tensor, +void RuntimeGraph::clearTensors() { _tensor_to_data.clear(); } + +void RuntimeGraph::makeInplaceOperation(const circle::Tensor *removing_tensor, const circle::Tensor *dst_tensor) { - if (src_tensor == nullptr or dst_tensor == nullptr) - return; + assert(_reader->get_current_subgraph_index() == _subgraph_index); + assert(removing_tensor != nullptr); - auto src_it = _tensor_to_data.find(src_tensor); + auto src_it = _tensor_to_data.find(removing_tensor); - assert(src_it != _tensor_to_data.end() && "Failed makeInplaceOperation"); + if (src_it == _tensor_to_data.end()) + return; - auto *data = _tensor_to_data[src_tensor]; + auto *data = _tensor_to_data[removing_tensor]; _tensor_to_data.erase(src_it); + if (dst_tensor == nullptr) + { + delete[] data; + return; + } + assert(_tensor_to_data.find(dst_tensor) == _tensor_to_data.end() && "Failed makeInplaceOperation"); _tensor_to_data[dst_tensor] = data; @@ -276,6 +370,7 @@ void RuntimeGraph::makeInplaceOperation(const circle::Tensor *src_tensor, uint8_t *RuntimeGraph::getConstDataByTensor(const circle::Tensor *raw_tensor) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); if (raw_tensor == nullptr) return nullptr; @@ -286,6 +381,7 @@ uint8_t *RuntimeGraph::getConstDataByTensor(const circle::Tensor *raw_tensor) const circle::Tensor *RuntimeGraph::getCircleTensorByIndex(int32_t index) { + assert(_reader->get_current_subgraph_index() == _subgraph_index); if (index < 0) return nullptr; @@ -294,9 +390,9 @@ const circle::Tensor *RuntimeGraph::getCircleTensorByIndex(int32_t index) return raw_tensor; } -void RuntimeGraph::configure() +void RuntimeGraph::configure(bool dealloc_input) { - KernelConfigureRegistry kernel_configure; + selectOwnSubgraph(); for (uint32_t i = 0; i < _reader->operators().size(); ++i) { @@ -309,33 +405,36 @@ void RuntimeGraph::configure() } if (not _is_valid) - buildAllocDeallocPlan(); + buildAllocDeallocPlan(dealloc_input); _is_valid = true; } +void RuntimeGraph::setDataToTensor(const circle::Tensor *tensor, uint8_t *data) +{ + _tensor_to_data[tensor] = data; +} + void RuntimeGraph::execute() { + selectOwnSubgraph(); + if (not _is_valid) - configure(); + configure(true); - KernelExecuteRegistry kernel_executor; + const auto operators_size = _reader->operators().size(); + const auto operators = _reader->operators(); - for (uint32_t i = 0; i < _reader->operators().size(); ++i) + for (uint32_t i = 0; i < operators_size; ++i) { - const auto op = _reader->operators().at(i); + const auto op = operators.at(i); assert(op != nullptr); const auto opcode = _reader->builtin_code(op); allocate(i); - bool is_inplace = false; - - if (_inplace_op_indexes.find(i) != _inplace_op_indexes.end()) - is_inplace = true; - - kernel_executor.execute_kernel(op, opcode, this, is_inplace); + kernel_executor.execute_kernel(op, opcode, this); deallocate(i); } diff --git a/onert-micro/luci-interpreter/src/core/RuntimeGraph.h b/onert-micro/luci-interpreter/src/core/RuntimeGraph.h index b64e8dc..baac0b1 100644 --- a/onert-micro/luci-interpreter/src/core/RuntimeGraph.h +++ b/onert-micro/luci-interpreter/src/core/RuntimeGraph.h @@ -57,7 +57,10 @@ public: class RuntimeGraph { public: - explicit RuntimeGraph(SimpleMemoryManager *memory_manager, CircleReader *circle_reader); + RuntimeGraph() = delete; + + explicit RuntimeGraph(SimpleMemoryManager *memory_manager, CircleReader *circle_reader, + RuntimeModule *runtime_module, uint32_t subgraph_index); ~RuntimeGraph(); Tensor *addTensor(const circle::Tensor *raw_tensor, std::unique_ptr &&tensor); @@ -75,35 +78,70 @@ public: int32_t getInputDataSizeByIndex(int32_t input_index); int32_t getOutputDataSizeByIndex(int32_t output_index); + int32_t getNumOfInputTensors(); + int32_t getNumOfOutputTensors(); + + const circle::Tensor *getInputTensorByIndex(int32_t input_index); + const circle::Tensor *getOutputTensorByIndex(int32_t input_index); + uint8_t *getOutputDataByIndex(int32_t output_index); - void addInplaceOpIndex(uint32_t index) { _inplace_op_indexes.insert(index); } + void addInplaceOpIndex(const circle::Operator *op) { _inplace_op_indexes.insert(op); } void execute(); - void configure(); + void configure(bool dealloc_input); void invalidate() { _is_valid = false; } bool isValid() const { return _is_valid; } + void selectOwnSubgraph() { _reader->select_subgraph(_subgraph_index); }; + void resetOutputTensorsData(); + + void clearTensors(); + + void setDataToTensor(const circle::Tensor *tensor, uint8_t *data); + + void resetTensorData(uint8_t *new_data, const circle::Tensor *tensor); + + RuntimeModule *getRuntimeModule() { return _runtime_module; }; + + bool is_inplace_op(const circle::Operator *op) + { + return _inplace_op_indexes.find(op) != _inplace_op_indexes.end(); + } + +#ifndef DIS_DYN_SHAPES + void addDynamicShapeTensor(const circle::Tensor *tensor, luci_interpreter::RuntimeShape &&shapes); + + luci_interpreter::RuntimeShape *getDynamicShapeTensor(const circle::Tensor *tensor); + + void removeDynamicShapeTensor(const circle::Tensor *tensor); +#endif // DIS_DYN_SHAPES + private: - void buildAllocDeallocPlan(); + void buildAllocDeallocPlan(bool dealloc_input); void allocate(size_t kernel_index); void deallocate(size_t kernel_index); - void resetOutputTensorsData(); - private: SimpleMemoryManager *_memory_manager; CircleReader *_reader; + RuntimeModule *_runtime_module; std::unordered_map _tensor_to_data; - std::unordered_set _inplace_op_indexes; + std::unordered_set _inplace_op_indexes; bool _is_valid = false; // Tensors that are not used anymore after given op std::vector> _alloc_plan; std::vector> _dealloc_plan; + + uint32_t _subgraph_index; + +#ifndef DIS_DYN_SHAPES + std::unordered_map _dynamic_tensor_shapes; +#endif // DIS_DYN_SHAPES }; #endif // USE_STATIC_ALLOC diff --git a/onert-micro/luci-interpreter/src/core/RuntimeModule.h b/onert-micro/luci-interpreter/src/core/RuntimeModule.h index 5dbe5a9..d426982 100644 --- a/onert-micro/luci-interpreter/src/core/RuntimeModule.h +++ b/onert-micro/luci-interpreter/src/core/RuntimeModule.h @@ -41,7 +41,8 @@ public: void addGraph(MemoryManager *memory_manager) { - _graphs.emplace_back(memory_manager, &_circle_reader); + _graphs.emplace_back(memory_manager, &_circle_reader, this, + _circle_reader.get_current_subgraph_index()); } BaseRuntimeGraph *getRuntimeGraphAt(uint32_t pos) { return &_graphs.at(pos); } @@ -52,6 +53,8 @@ public: BaseRuntimeGraph *getMainGraph() const { return const_cast(&_graphs[0]); } + void selectSubgraph(uint32_t index) { _circle_reader.select_subgraph(index); } + private: std::vector _graphs; diff --git a/onert-micro/luci-interpreter/src/core/reader/CircleMicroReader.cpp b/onert-micro/luci-interpreter/src/core/reader/CircleMicroReader.cpp index 332e464..06d93f2 100644 --- a/onert-micro/luci-interpreter/src/core/reader/CircleMicroReader.cpp +++ b/onert-micro/luci-interpreter/src/core/reader/CircleMicroReader.cpp @@ -139,6 +139,8 @@ bool CircleReader::select_subgraph(uint32_t sgindex) _current_subgraph = subgraphs->Get(sgindex); assert(_current_subgraph != nullptr); + _current_subgraph_index = sgindex; + return true; } diff --git a/onert-micro/luci-interpreter/src/kernels/Abs.cpp b/onert-micro/luci-interpreter/src/kernels/Abs.cpp new file mode 100644 index 0000000..98acd13 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/Abs.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Builders.h" +#include "kernels/Utils.h" +#include "PALAbs.h" + +namespace luci_interpreter +{ + +void configure_kernel_CircleAbs(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + auto output = runtime_graph->getCircleTensorByIndex(output_index); + + assert(input != nullptr); + assert(output != nullptr); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == Tensor::element_type(output)); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(input) == Tensor::num_dims(output)); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(input) == Tensor::num_elements(output)); +} + +void execute_kernel_CircleAbs(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + auto output = runtime_graph->getCircleTensorByIndex(output_index); + + assert(input != nullptr); + assert(output != nullptr); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + const uint8_t *input_data = runtime_graph->getDataByTensor(input); + uint8_t *output_data = runtime_graph->getDataByTensor(output); + + if (is_inplace) + { + output_data = const_cast(input_data); + } + + assert(input_data != nullptr); + assert(output_data != nullptr); + + const int flat_size = kernels::getTensorRuntimeShape(input, runtime_graph).flatSize(); + + switch (Tensor::element_type(input)) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + luci_interpreter_pal::Abs(flat_size, kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); + } + + if (is_inplace) + { + runtime_graph->makeInplaceOperation(input, output); + } +} + +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Abs.test.cpp b/onert-micro/luci-interpreter/src/kernels/Abs.test.cpp new file mode 100644 index 0000000..1e44e2f --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/Abs.test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernels/TestUtils.h" +#include "luci_interpreter/test_models/abs/FloatAbsKernel.h" +#include "luci_interpreter/test_models/abs/NegAbsKernel.h" + +#include "loader/ModuleLoader.h" + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +class AbsTest : public ::testing::Test +{ + // Do nothing +}; + +template std::vector checkAbsKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(AbsTest, Float_P) +{ + test_kernel::TestDataFloatAbs test_data_kernel; + std::vector output_data_vector = checkAbsKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} + +TEST_F(AbsTest, Input_output_type_mismatch_NEG) +{ + test_kernel::NegTestDataInputOutputTypeMismatchAbsKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(AbsTest, Input_output_shape_mismatch_NEG) +{ + test_kernel::NegTestDataInputOutputShapeMismatchAbsKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +} // namespace +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Add.cpp b/onert-micro/luci-interpreter/src/kernels/Add.cpp index 07b6c20..6e0398c 100644 --- a/onert-micro/luci-interpreter/src/kernels/Add.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Add.cpp @@ -15,205 +15,108 @@ * limitations under the License. */ -#include "kernels/Add.h" +#include "Builders.h" +#include "kernels/Utils.h" #include "kernels/BinaryOpCommon.h" -#include "kernels/Utils.h" -#include -#include +#include "PALAdd.h" namespace luci_interpreter { -namespace kernels -{ - -Add::Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams ¶ms) - : KernelWithParams({input1, input2}, {output}, params) -{ -} - -void Add::configure() -{ - LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); - LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); - if (input1()->element_type() == DataType::S16) - { - LUCI_INTERPRETER_CHECK(input1()->zero_points().size() == 1 && - input2()->zero_points().size() == 1); - LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 && - output()->zero_point() == 0); - } - - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); -} - -void Add::execute() const -{ - switch (input1()->element_type()) - { - case DataType::FLOAT32: - evalFloat(); - break; - case DataType::S64: - evalInteger(); - break; - case DataType::S32: - evalInteger(); - break; - case DataType::U8: - evalQuantized(); - break; - case DataType::S16: - evalQuantizedS16(); - break; - default: - assert(false && "Unsupported type."); - } -} - -void Add::evalFloat() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - if (need_broadcast) - { - tflite::reference_ops::BroadcastAdd4DSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -template void Add::evalInteger() const +void configure_kernel_CircleAdd(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); + kernels::TISOKernel kernel(cur_op, runtime_graph); - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); - if (need_broadcast) +#ifndef DIS_QUANT + if (Tensor::element_type(kernel.input1()) == DataType::S16) { - tflite::reference_ops::BroadcastAdd4DSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); + LUCI_INTERPRETER_CHECK(Tensor::zero_points(kernel.input1()).size() == 1 && + Tensor::zero_points(kernel.input2()).size() == 1); + LUCI_INTERPRETER_CHECK(Tensor::zero_point(kernel.input1()) == 0 && + Tensor::zero_point(kernel.input2()) == 0 && + Tensor::zero_point(kernel.output()) == 0); } +#endif // DIS_QUANT } -void Add::evalQuantized() const +void execute_kernel_CircleAdd(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto input1_scale = static_cast(input1()->scale()); - const auto input2_scale = static_cast(input2()->scale()); - const auto output_scale = static_cast(output()->scale()); - - const int left_shift = 20; - const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); - const double real_input1_multiplier = input1_scale / twice_max_input_scale; - const double real_input2_multiplier = input2_scale / twice_max_input_scale; - const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); - - int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; - int input1_shift{}, input2_shift{}, output_shift{}; - quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); - quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); - quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); + kernels::TISOKernel kernel(cur_op, runtime_graph); - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); + const auto *options = cur_op->builtin_options_as_AddOptions(); - 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; + luci_interpreter::RuntimeShape input_shape1 = + kernels::getTensorRuntimeShape(kernel.input1(), runtime_graph); + luci_interpreter::RuntimeShape input_shape2 = + kernels::getTensorRuntimeShape(kernel.input2(), runtime_graph); - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + bool is_inplace = runtime_graph->is_inplace_op(cur_op); - if (need_broadcast) + switch (Tensor::element_type(kernel.input1())) { - tflite::reference_ops::BroadcastAdd4DSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + auto tiso_func = luci_interpreter_pal::Add; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastAdd4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +#endif // DIS_FLOAT + case DataType::S64: + { + auto tiso_func = luci_interpreter_pal::Add; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastAdd4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; + case DataType::S32: + { + auto tiso_func = luci_interpreter_pal::Add; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastAdd4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; + default: + assert(false && "Unsupported type."); } } -void Add::evalQuantizedS16() const -{ - const auto input1_scale = static_cast(input1()->scale()); - const auto input2_scale = static_cast(input2()->scale()); - const auto output_scale = static_cast(output()->scale()); - - constexpr int left_shift = 12; - const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); - const double real_input1_multiplier = input1_scale / twice_max_input_scale; - const double real_input2_multiplier = input2_scale / twice_max_input_scale; - const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); - - int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; - int input1_shift{}, input2_shift{}, output_shift{}; - quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); - quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); - quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); - - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - auto fn = [input1_multiplier, input1_shift, // - input2_multiplier, input2_shift, // - output_multiplier, output_shift, // - activation_min, activation_max](int16_t input1_val, int16_t input2_val) { - const int32_t shifted_input1_val = static_cast(input1_val) << left_shift; - const int32_t shifted_input2_val = static_cast(input2_val) << left_shift; - const int32_t scaled_input1_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input1_val, input1_multiplier, input1_shift); - const int32_t scaled_input2_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input2_val, input2_multiplier, input2_shift); - const int32_t raw_sum = scaled_input1_val + scaled_input2_val; - const int32_t raw_output = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - raw_sum, output_multiplier, output_shift); - const int32_t clamped_output = std::min(activation_max, std::max(activation_min, raw_output)); - return static_cast(clamped_output); - }; - - BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output()), fn); -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Add.h b/onert-micro/luci-interpreter/src/kernels/Add.h deleted file mode 100644 index f921717..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Add.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_ADD_H -#define LUCI_INTERPRETER_KERNELS_ADD_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Add : public KernelWithParams -{ -public: - Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams ¶ms); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; - void evalQuantizedS16() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_ADD_H diff --git a/onert-micro/luci-interpreter/src/kernels/Add.test.cpp b/onert-micro/luci-interpreter/src/kernels/Add.test.cpp index b8b1c30..6df81d3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Add.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Add.test.cpp @@ -15,14 +15,15 @@ * limitations under the License. */ -#include "kernels/Add.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/add/FloatAddKernel.h" +#include "luci_interpreter/test_models/add/IntAddKernel.h" +#include "luci_interpreter/test_models/add/NegAddKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,328 +31,136 @@ using namespace testing; class AddTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -// for quantized Add, the error shouldn't exceed step -float GetTolerance(float min, float max) +template std::vector checkAddKernel(test_kernel::TestDataBase *test_data_base) { - float kQuantizedStep = (max - min) / 255.0; - return kQuantizedStep; -} - -TEST_F(AddTest, Uint8) -{ - std::initializer_list base_shape = {2, 3, 1, 2}; - std::initializer_list base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - std::initializer_list test_shapes[] = { - {1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::initializer_list test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - std::initializer_list output_shapes[] = { - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; - std::vector> output_data = { - {-0.1f, 2.6f, -0.7f, 2.8f, 0.7f, 3.0f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, -0.8f, 0.4f, -0.6f, 1.8f, -0.2f, 1.4f, 3.0f, 0.8f, 3.0f, 2.2f, 3.0f, - -1.4f, 0.3f, -2.0f, 0.5f, -0.6f, 0.9f, 0.9f, -1.9f, 0.3f, -1.7f, 1.7f, -1.3f}, - {-0.1f, 2.6f, 0.5f, 1.0f, 1.8f, -0.2f, 1.4f, 3.0f, -2.0f, 0.5f, 1.7f, -1.3f}, - {-0.1f, 2.5f, 0.0f, 2.6f, -0.7f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, -0.9f, 1.1f, -0.8f, 0.4f, -1.5f, 1.7f, 3.0f, 2.2f, 3.0f, 2.1f, 3.0f, - -1.1f, 0.5f, -0.6f, 1.0f, -0.7f, 0.9f, 1.2f, -1.7f, 1.7f, -1.2f, 1.6f, -1.3f}, - {-0.1f, 2.5f, 1.2f, 0.8f, 0.4f, -1.5f, 1.7f, 3.0f, -0.6f, 1.0f, 1.6f, -1.3f}}; - float kQuantizedTolerance = GetTolerance(-3.f, 3.f); - std::pair quant_param = quantizationParams(-3.f, 3.f); - for (int i = 0; i < output_data.size(); i++) - { - Tensor input1_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - AddParams params{}; - params.activation = Activation::NONE; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data[i], kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - // Re-run with exchanged inputs. - for (int i = 0; i < output_data.size(); i++) + + // set right input data { - Tensor input1_tensor = makeInputTensor( - test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - AddParams params{}; - params.activation = Activation::NONE; + runtime_module.execute(); - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data[i], kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); - } + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(AddTest, Float) +TEST_F(AddTest, Float_P) { - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> test_outputs = { - {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, - 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, - {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, - {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, - 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, - {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; - std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestDataFloatAdd test_data_float_add_no_broadcasting(is_with_broadcast); + std::vector output_data_vector = checkAddKernel(&test_data_float_add_no_broadcasting); + EXPECT_THAT(output_data_vector, + kernels::testing::FloatArrayNear( + test_data_float_add_no_broadcasting.get_output_data_by_index(0), 0.0001f)); } - // Re-run with exchanged inputs. - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = - makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestDataFloatAdd test_data_float_add_with_broadcasting(is_with_broadcast); + std::vector output_data_vector = checkAddKernel(&test_data_float_add_with_broadcasting); + EXPECT_THAT(output_data_vector, + kernels::testing::FloatArrayNear( + test_data_float_add_with_broadcasting.get_output_data_by_index(0), 0.0001f)); } } -template void CheckInteger(luci_interpreter::IMemoryManager *memory_manager) +TEST_F(AddTest, INT64_P) { - using dtype = typename loco::DataTypeImpl::Type; - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> test_outputs = { - {3, 3, 0, 1, 0, 8, 5, 1, 0, 0, 2, 6, 8, 0, 1, 0, 5, 1, - 5, 4, 0, 2, 2, 9, 11, 0, 4, 0, 8, 5, 11, 2, 4, 0, 8, 7}, - {3, 3, 0, 0, 5, 1, 5, 4, 4, 0, 8, 7}, - {3, 6, 0, 3, 0, 0, 5, 4, 2, 1, 0, 0, 8, 0, 5, 0, 1, 0, - 0, 2, 2, 4, 7, 9, 6, 0, 8, 0, 13, 5, 6, 0, 8, 2, 13, 7}, - {3, 6, 2, 1, 1, 0, 0, 2, 8, 0, 13, 7}}; - std::vector input1_data{-1, 2, 1, 0, 4, -5, 1, 3, 7, -1, 7, 1}; - std::vector input2_data{4, 1, -3, -1, 1, 6}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestData64IntAdd test_data_int64_add_no_broadcasting(is_with_broadcast); + const auto output_data_vector = checkAddKernel(&test_data_int64_add_no_broadcasting); + EXPECT_THAT(output_data_vector, + test_data_int64_add_no_broadcasting.get_output_data_by_index(0)); } - // Re-run with exchanged inputs. - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor input2_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestData64IntAdd test_data_int64_add_with_broadcasting(is_with_broadcast); + const auto output_data_vector = checkAddKernel(&test_data_int64_add_with_broadcasting); + EXPECT_THAT(output_data_vector, + test_data_int64_add_with_broadcasting.get_output_data_by_index(0)); } -}; - -TEST_F(AddTest, SInt32) -{ - CheckInteger(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(AddTest, SInt64) -{ - CheckInteger(_memory_manager.get()); - SUCCEED(); } -TEST_F(AddTest, SInt16) +TEST_F(AddTest, INT32_P) { - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> ref_output_shapes{ - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; - - std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - std::vector> ref_outputs = { - {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, - 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, - {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, - {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, - 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, - {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; - - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, 3.0 / 32767, 0, input1_data, - _memory_manager.get()); - Tensor input2_tensor = makeInputTensor(test_shapes[i], 1.0 / 32767, 0, - input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); - const float tolerance = output_tensor.scale(); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), - ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestData32IntAdd test_data_int32_add_no_broadcasting(is_with_broadcast); + const auto output_data_vector = checkAddKernel(&test_data_int32_add_no_broadcasting); + EXPECT_THAT(output_data_vector, + test_data_int32_add_no_broadcasting.get_output_data_by_index(0)); } - // Re-run with exchanged inputs and different scales. - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = makeInputTensor(test_shapes[i], 2.0 / 32767, 0, - input2_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor(base_shape, 4.0 / 32767, 0, input1_data, - _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 5.0 / 32767, 0); - const float tolerance = output_tensor.scale(); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), - ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestData32IntAdd test_data_int32_add_with_broadcasting(is_with_broadcast); + const auto output_data_vector = checkAddKernel(&test_data_int32_add_with_broadcasting); + EXPECT_THAT(output_data_vector, + test_data_int32_add_with_broadcasting.get_output_data_by_index(0)); } } -TEST_F(AddTest, Input_Output_Type_NEG) +TEST_F(AddTest, Input_type_mismatch_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(AddTest, Invalid_Output_Type_NEG) -{ - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputMismatchAddKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(AddTest, Invalid_Input_Type_NEG) +TEST_F(AddTest, No_quant_params_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U64); - - AddParams params{}; - params.activation = Activation::RELU; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataNoQuantParamsS16AddKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(AddTest, Invalid_Quantization_NEG) -{ - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16); - - AddParams params{}; - params.activation = Activation::NONE; - - Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add tests for U8 and S16 +// TODO: add tests for inplace optimizations for all types } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/AddN.cpp b/onert-micro/luci-interpreter/src/kernels/AddN.cpp new file mode 100644 index 0000000..0df3c44 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/AddN.cpp @@ -0,0 +1,113 @@ +/* + * 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 "Builders.h" +#include "kernels/Utils.h" + +#include "PALAddN.h" + +namespace luci_interpreter +{ + +namespace +{ + +template +void evalGeneric(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const auto output_index = cur_op->outputs()->operator[](0); + assert(output_index != -1); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + const auto input_sizes = cur_op->inputs()->size(); + + auto input1_index = cur_op->inputs()->operator[](0); + const auto *tensor1 = runtime_graph->getCircleTensorByIndex(input1_index); + + const int flat_size = Tensor::num_elements(tensor1); + + std::vector all_input_data; + for (int32_t i = 0; i < input_sizes; ++i) + { + auto input_index = cur_op->inputs()->operator[](i); + const auto *tensor = runtime_graph->getCircleTensorByIndex(input_index); + + const auto *tensor_data = runtime_graph->getDataByTensor(tensor); + if (tensor_data == nullptr) + tensor_data = runtime_graph->getConstDataByTensor(tensor); + + auto *data = reinterpret_cast(tensor_data); + all_input_data.push_back(data); + } + + auto *output_data = reinterpret_cast(runtime_graph->getDataByTensor(output)); + + luci_interpreter_pal::AddN(flat_size, input_sizes, all_input_data.data(), output_data); +} + +} // namespace + +void configure_kernel_CircleAddN(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const int num_inputs = cur_op->inputs()->size(); + + LUCI_INTERPRETER_CHECK(num_inputs >= 2); + LUCI_INTERPRETER_CHECK(cur_op->outputs()->size() == 1); + + const auto input1_index = cur_op->inputs()->operator[](0); + assert(input1_index != -1); + + const auto input1_tensor = runtime_graph->getCircleTensorByIndex(input1_index); + assert(input1_tensor != nullptr); + + for (int i = 1; i < num_inputs; ++i) + { + const auto input_index = cur_op->inputs()->operator[](i); + assert(input_index != -1); + + const auto input_tensor = runtime_graph->getCircleTensorByIndex(input_index); + assert(input_tensor != nullptr); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(input1_tensor) == + Tensor::element_type(input_tensor)); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(input1_tensor) == Tensor::num_dims(input_tensor)); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(input1_tensor) == + Tensor::num_elements(input_tensor)); + } +} + +void execute_kernel_CircleAddN(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const auto output_index = cur_op->outputs()->operator[](0); + assert(output_index != -1); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + switch (Tensor::element_type(output)) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + evalGeneric(cur_op, runtime_graph); + } + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); + } +} + +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/AddN.test.cpp b/onert-micro/luci-interpreter/src/kernels/AddN.test.cpp new file mode 100644 index 0000000..cee0bbc --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/AddN.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernels/TestUtils.h" +#include "luci_interpreter/test_models/add_n/FloatAddNKernel.h" +#include "luci_interpreter/test_models/add_n/NegAddNKernel.h" + +#include "loader/ModuleLoader.h" + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +class AddNTest : public ::testing::Test +{ + // Do nothing +}; + +template std::vector checkAddNKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 3); + + // set 1 input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + // set 2 input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } + + // set 3 input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(2)); + std::copy(test_data_base->get_input_data_by_index(2).begin(), + test_data_base->get_input_data_by_index(2).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(AddNTest, Float_P) +{ + test_kernel::TestDataFloatAddN test_data_kernel; + std::vector output_data_vector = checkAddNKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); +} + +TEST_F(AddNTest, InputTypeMismatch_NEG) +{ + test_kernel::TestDataInputTypeMismatchAddN test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +} // namespace +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ArgMax.cpp b/onert-micro/luci-interpreter/src/kernels/ArgMax.cpp index 5ac4bcb..be6291c 100644 --- a/onert-micro/luci-interpreter/src/kernels/ArgMax.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ArgMax.cpp @@ -15,127 +15,55 @@ * limitations under the License. */ -#include "kernels/ArgMax.h" +#include "Builders.h" #include "kernels/Utils.h" -#include "PALArgMax.h" +#include "TISOKernel.h" + +#include "PALArgMinMax.h" namespace luci_interpreter { -namespace kernels -{ -ArgMax::ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams ¶ms) - : KernelWithParams({input, axis}, {output}, params) +void configure_kernel_CircleArgMax(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + kernels::TISOKernel kernel(cur_op, runtime_graph); + // dim tensor must be a scalar or has one element + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input2()) == 0 or + Tensor::num_elements(kernel.input2()) == 1); + // value and output type must match + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::S32); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32); } -void ArgMax::configure() +void execute_kernel_CircleArgMax(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - assert(axis()->element_type() == DataType::S32 || axis()->element_type() == DataType::S64); - assert(input()->shape().num_dims() >= 1); - const Shape &input_shape = input()->shape(); - const int num_dims = input_shape.num_dims(); - Shape output_shape(num_dims - 1); - - // If axis value is negative, then update by adding input_shape's num_dims. - // If updated value also negative, then assert. - assert(axis()->shape().num_elements() == 1); - int axis_value = getTensorData(axis())[0]; - if (axis_value < 0) - axis_value = axis_value + num_dims; - assert(axis_value >= 0); - - int j = 0; - for (int i = 0; i < num_dims; i++) - { - if (i == axis_value) - continue; - output_shape.dim(j++) = input_shape.dim(i); - } + kernels::TISOKernel kernel(cur_op, runtime_graph); - assert(output()->element_type() == _params.output_type); + const circle::Tensor *input = kernel.input1(); + const circle::Tensor *output = kernel.output(); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); -} - -void ArgMax::execute() const -{ + kernels::TISOData tiso_data = kernel.readData(); + const auto input_data = tiso_data.input1_data; + const auto axis_data = tiso_data.input2_data; + auto output_data = tiso_data.output_data; -#define TF_LITE_ARG_MAX(data_type, axis_type, output_type) \ - luci_interpreter_pal::ArgMinMax(getTensorShape(input()), getTensorData(input()), \ - getTensorData(axis()), getTensorShape(output()), \ - getTensorData(output()), std::greater()) - if (axis()->element_type() == DataType::S32) - { - switch (_params.output_type) - { - case DataType::S32: - switch (input()->element_type()) - { - case DataType::FLOAT32: - TF_LITE_ARG_MAX(float, int32_t, int32_t); - break; - case DataType::U8: - TF_LITE_ARG_MAX(uint8_t, int32_t, int32_t); - break; - default: - assert(false && "Unsupported input type."); - } - break; - case DataType::S64: - switch (input()->element_type()) - { - case DataType::FLOAT32: - TF_LITE_ARG_MAX(float, int32_t, int64_t); - break; - case DataType::U8: - TF_LITE_ARG_MAX(uint8_t, int32_t, int64_t); - break; - default: - assert(false && "Unsupported input type."); - } - break; - default: - assert(false && "Unsupported output type."); - } - } - else + switch (Tensor::element_type(input)) { - switch (_params.output_type) +#ifndef DIS_FLOAT + case DataType::FLOAT32: { - case DataType::S32: - switch (input()->element_type()) - { - case DataType::FLOAT32: - TF_LITE_ARG_MAX(float, int64_t, int32_t); - break; - case DataType::U8: - TF_LITE_ARG_MAX(uint8_t, int64_t, int32_t); - break; - default: - assert(false && "Unsupported input type."); - } - break; - case DataType::S64: - switch (input()->element_type()) - { - case DataType::FLOAT32: - TF_LITE_ARG_MAX(float, int64_t, int64_t); - break; - case DataType::U8: - TF_LITE_ARG_MAX(uint8_t, int64_t, int64_t); - break; - default: - assert(false && "Unsupported input type."); - } - break; - default: - assert(false && "Unsupported output type."); + luci_interpreter_pal::ArgMinMax( + kernels::getTensorRuntimeShape(input, runtime_graph), + kernels::getTensorData(input_data), kernels::getTensorData(axis_data), + kernels::getTensorRuntimeShape(output, runtime_graph), + kernels::getTensorData(output_data), std::greater()); } + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported ArgMax input type"); } -#undef TF_LITE_ARG_MAX } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ArgMax.h b/onert-micro/luci-interpreter/src/kernels/ArgMax.h deleted file mode 100644 index 0ee812d..0000000 --- a/onert-micro/luci-interpreter/src/kernels/ArgMax.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_ARGMAX_H -#define LUCI_INTERPRETER_KERNELS_ARGMAX_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class ArgMax : public KernelWithParams -{ -public: - ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams ¶ms); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *axis() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_ARGMAX_H diff --git a/onert-micro/luci-interpreter/src/kernels/ArgMax.test.cpp b/onert-micro/luci-interpreter/src/kernels/ArgMax.test.cpp index 2089878..297ae29 100644 --- a/onert-micro/luci-interpreter/src/kernels/ArgMax.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ArgMax.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,109 +14,73 @@ * limitations under the License. */ -#include "kernels/ArgMax.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/argmax/FloatArgMaxKernel.h" +#include "luci_interpreter/test_models/argmax/NegArgMaxKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template -void Check(std::initializer_list input_shape, - std::initializer_list dimension_shape, - std::initializer_list output_shape, std::initializer_list input_data, - std::initializer_list dimension_data, std::initializer_list output_data) +class ArgMaxTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor dimension_tensor = - makeInputTensor(dimension_shape, dimension_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(getElementType()); + // Do nothing +}; - ArgMaxParams params{}; - params.output_type = getElementType(); - ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); +template +std::vector checkKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), output_shape); -} + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); -template class ArgMaxTest : public ::testing::Test -{ -}; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(ArgMaxTest, DataTypes); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -TYPED_TEST(ArgMaxTest, Simple) -{ - Check(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{}, - /*output_shape=*/{1, 1, 1}, - /*input_data=*/ - { - 1, 9, 7, 3, // - }, - /*dimension_data=*/{3}, /*output_data=*/{1}); - Check(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{}, - /*output_shape=*/{1, 1, 1}, - /*input_data=*/ - { - 1, 9, 7, 3, // - }, - /*dimension_data=*/{3}, /*output_data=*/{1}); + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + O *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(O)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TYPED_TEST(ArgMaxTest, MultiDimensions) +TEST_F(ArgMaxTest, MainTest_P) { - Check(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{}, - /*output_shape=*/{1, 1, 2}, - /*input_data=*/ - { - 1, 2, 7, 8, // - 1, 9, 7, 3, // - }, - /*dimension_data=*/{3}, /*output_data=*/{3, 1}); - Check(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{}, - /*output_shape=*/{1, 1, 2}, - /*input_data=*/ - { - 1, 2, 7, 8, // - 1, 9, 7, 3, // - }, - /*dimension_data=*/{3}, /*output_data=*/{3, 1}); + test_kernel::TestDataFloatArgMax test_data_kernel; + std::vector output_data_vector = checkKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(ArgMaxTest, UnsupportedType_NEG) +TEST_F(ArgMaxTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input_tensor = makeInputTensor({1, 1, 2, 4}, - { - 1, 2, 7, 8, // - 1, 9, 7, 3, // - }, - memory_manager.get()); - Tensor dimension_tensor = makeInputTensor({}, {3}, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - ArgMaxParams params{}; - params.output_type = DataType::U8; - ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::TestDataOutputWrongOutputArgMax test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ArgMin.cpp b/onert-micro/luci-interpreter/src/kernels/ArgMin.cpp new file mode 100644 index 0000000..ed12496 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/ArgMin.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 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 "Builders.h" +#include "kernels/Utils.h" +#include "TISOKernel.h" + +#include "PALArgMinMax.h" + +namespace luci_interpreter +{ + +// TODO: reduce code duplication with arg max +void configure_kernel_CircleArgMin(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); + // dim tensor must be a scalar or has one element + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input2()) == 0 or + Tensor::num_elements(kernel.input2()) == 1); + // value and output type must match + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::S32); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32); +} + +void execute_kernel_CircleArgMin(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); + + const circle::Tensor *input = kernel.input1(); + const circle::Tensor *output = kernel.output(); + + kernels::TISOData tiso_data = kernel.readData(); + const auto input_data = tiso_data.input1_data; + const auto axis_data = tiso_data.input2_data; + auto output_data = tiso_data.output_data; + + switch (Tensor::element_type(input)) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + luci_interpreter_pal::ArgMinMax( + kernels::getTensorRuntimeShape(input, runtime_graph), + kernels::getTensorData(input_data), kernels::getTensorData(axis_data), + kernels::getTensorRuntimeShape(output, runtime_graph), + kernels::getTensorData(output_data), std::less()); + } + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported ArgMax input type"); + } +} + +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ArgMin.test.cpp b/onert-micro/luci-interpreter/src/kernels/ArgMin.test.cpp new file mode 100644 index 0000000..b275663 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/ArgMin.test.cpp @@ -0,0 +1,86 @@ +/* + * 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/TestUtils.h" +#include "luci_interpreter/test_models/argmin/FloatArgMinKernel.h" +#include "luci_interpreter/test_models/argmin/NegArgMinKernel.h" + +#include "loader/ModuleLoader.h" + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +class ArgMinTest : public ::testing::Test +{ + // Do nothing +}; + +template +std::vector checkKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + O *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(O)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(ArgMinTest, MainTest_P) +{ + test_kernel::TestDataFloatArgMin test_data_kernel; + std::vector output_data_vector = checkKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} + +TEST_F(ArgMinTest, Input_output_type_mismatch_NEG) +{ + test_kernel::TestDataOutputWrongOutputArgMin test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +} // namespace +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.cpp b/onert-micro/luci-interpreter/src/kernels/AveragePool2D.cpp index 6dd81ab..ccdda84 100644 --- a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.cpp +++ b/onert-micro/luci-interpreter/src/kernels/AveragePool2D.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,180 +14,87 @@ * limitations under the License. */ -#include "kernels/AveragePool2D.h" +#include "Builders.h" #include "kernels/Utils.h" - -#include "PALAveragePool2d.h" +#include "PALAveragePool2D.h" namespace luci_interpreter { -namespace kernels +// TODO: reduce code duplication with MaxPool2D +void configure_kernel_CircleAveragePool2D(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); -AveragePool2D::AveragePool2D(const Tensor *input, Tensor *output, Tensor *scratchpad, - const Pool2DParams ¶ms) - : KernelWithParams({input}, {output, scratchpad}, params) -{ -} + assert(input_index != -1); + assert(output_index != -1); -void AveragePool2D::configure() -{ - if (input()->element_type() != output()->element_type()) - { - assert(false && "Input Tensor and Output Tensor Type must be same"); - } - if (input()->shape().num_dims() != 4) - { - assert(false && "Input Tensor Shape must be 4-D"); - } - const Shape &input_shape = input()->shape(); - - const int32_t batches = input_shape.dim(0); - const int32_t input_height = input_shape.dim(1); - const int32_t input_width = input_shape.dim(2); - const int32_t depth = input_shape.dim(3); - - const int32_t output_height = - computeOutputSize(_params.padding, input_height, _params.filter_height, _params.stride_height); - const int32_t output_width = - computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); - - _padding_height = - computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); - _padding_width = - computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); - if (input()->element_type() == DataType::U8) - { - LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); - LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point()); - } - else if (input()->element_type() == DataType::S16) - { - LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); - LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); - } - else if (input()->element_type() == DataType::S8) - { - LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); - LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point()); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize({batches, output_height, output_width, depth}); + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); - auto scratchpad = getOutputTensors()[1]; - luci_interpreter_pal::SetupScratchpadTensor(scratchpad, input()->element_type(), - getTensorShape(input()), getTensorShape(output())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == Tensor::element_type(output)); + assert(Tensor::num_dims(input) == 4); } -void AveragePool2D::execute() const +void execute_kernel_CircleAveragePool2D(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - switch (input()->element_type()) - { - case DataType::FLOAT32: - evalFloat(); - break; - case DataType::U8: - evalQuantized(); - break; - case DataType::S16: - evalSInt16(); - break; - case DataType::S8: - evalSInt8(); - break; - default: - assert(false && "Unsupported type."); - } -} + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + auto output = runtime_graph->getCircleTensorByIndex(output_index); + + const auto *options = cur_op->builtin_options_as_Pool2DOptions(); + + const int32_t input_height = Tensor::dim(input, 1); + const int32_t input_width = Tensor::dim(input, 2); + + const int32_t output_height = kernels::computeOutputSize( + luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); + const int32_t output_width = kernels::computeOutputSize( + luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); + + const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, + options->filter_height(), output_height); + const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, + options->filter_width(), output_width); + + const auto *input_data = runtime_graph->getDataByTensor(input); + auto *output_data = runtime_graph->getDataByTensor(output); -void AveragePool2D::evalFloat() const -{ float activation_min{}; float activation_max{}; - calculateActivationRange(_params.activation, &activation_min, &activation_max); - - tflite::PoolParams params{}; - params.padding_values.height = _padding_height; - params.padding_values.width = _padding_width; - params.stride_height = _params.stride_height; - params.stride_width = _params.stride_width; - params.filter_height = _params.filter_height; - params.filter_width = _params.filter_width; + kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), + &activation_min, &activation_max); + luci_interpreter_pal::PoolParams params{}; + params.padding_values.height = padding_height; + params.padding_values.width = padding_width; + params.stride_height = options->stride_h(); + params.stride_width = options->stride_w(); + params.filter_height = options->filter_height(); + params.filter_width = options->filter_width(); params.float_activation_min = activation_min; params.float_activation_max = activation_max; - tflite::reference_ops::AveragePool(params, getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); -} - -void AveragePool2D::evalQuantized() const -{ - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - tflite::PoolParams params{}; - params.padding_values.height = _padding_height; - params.padding_values.width = _padding_width; - params.stride_height = _params.stride_height; - params.stride_width = _params.stride_width; - params.filter_height = _params.filter_height; - params.filter_width = _params.filter_width; - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - tflite::reference_ops::AveragePool(params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); -} - -void AveragePool2D::evalSInt8() const -{ - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - tflite::PoolParams params{}; - params.padding_values.height = _padding_height; - params.padding_values.width = _padding_width; - params.stride_height = _params.stride_height; - params.stride_width = _params.stride_width; - params.filter_height = _params.filter_height; - params.filter_width = _params.filter_width; - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - auto scratchpad = getOutputTensors()[1]; - int8_t *scratchpad_data = nullptr; - if (scratchpad->is_allocatable()) - scratchpad_data = scratchpad->data(); - - luci_interpreter_pal::AveragePool( - params, getTensorShape(input()), getTensorData(input()), getTensorShape(output()), - getTensorData(output()), getTensorShape(scratchpad), scratchpad_data); -} - -void AveragePool2D::evalSInt16() const -{ - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - tflite::PoolParams params{}; - params.padding_values.height = _padding_height; - params.padding_values.width = _padding_width; - params.stride_height = _params.stride_height; - params.stride_width = _params.stride_width; - params.filter_height = _params.filter_height; - params.filter_width = _params.filter_width; - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - tflite::reference_integer_ops::AveragePool( - params, getTensorShape(input()), getTensorData(input()), // - getTensorShape(output()), getTensorData(output())); + switch (Tensor::element_type(input)) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + luci_interpreter_pal::AveragePool( + params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); + } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.h b/onert-micro/luci-interpreter/src/kernels/AveragePool2D.h deleted file mode 100644 index 9e26c35..0000000 --- a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H -#define LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class AveragePool2D : public KernelWithParams -{ -public: - AveragePool2D(const Tensor *input, Tensor *output, Tensor *scratchpad, - const Pool2DParams ¶ms); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - void evalQuantized() const; - void evalSInt16() const; - void evalSInt8() const; - -private: - int32_t _padding_height{}; - int32_t _padding_width{}; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H diff --git a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp b/onert-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp index 1bd058e..6bc3264 100644 --- a/onert-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/AveragePool2D.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,14 +14,14 @@ * limitations under the License. */ -#include "kernels/AveragePool2D.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/average_pool_2d/FloatAveragePool2DKernel.h" +#include "luci_interpreter/test_models/average_pool_2d/NegAveragePool2DKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,255 +29,60 @@ using namespace testing; class AveragePool2DTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(AveragePool2DTest, Float) -{ - Shape input_shape{1, 3, 5, 1}; - std::vector input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - kernel.configure(); - _memory_manager->allocate_memory(scratchpad); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 0, 1.5, // - 4.5, 6, // - }; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1})); -} - -TEST_F(AveragePool2DTest, Uint8_0) -{ - std::vector input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // - }; - std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); - Tensor scratchpad(DataType::U8, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - kernel.configure(); - _memory_manager->allocate_memory(scratchpad); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0.0, 6.0})); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1})); -} - -TEST_F(AveragePool2DTest, Uint8_1) -{ - std::vector input_data{ - 0, 6, 12, 4, // - 3, 2, 10, 7, // - }; - - std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); - Tensor scratchpad(DataType::U8, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(scratchpad); - kernel.execute(); - - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({2.75, 6.0})); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1})); -} - -TEST_F(AveragePool2DTest, SInt16) -{ - Shape input_shape{1, 3, 5, 1}; - std::vector ref_output_shape{1, 2, 2, 1}; - std::vector input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // - }; - std::vector ref_output_data{ - 0, 1.5, // - 4.5, 6, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, 0.5, 0, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); - Tensor scratchpad(DataType::S16, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - kernel.configure(); - _memory_manager->allocate_memory(scratchpad); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); -} - -TEST_F(AveragePool2DTest, SInt8) +template +std::vector checkAveragePool2DKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 4, 5, 1}; - std::vector ref_output_shape{1, 2, 2, 1}; - std::vector input_data{-7, -3, 0, 2, -5, 12, -15, 3, 10, 5, - 7, -6, -1, 9, -2, 0, -5, 11, -1, -7}; - std::vector ref_output_data{ - 0, 2.5, // - 1, 1.5, // - }; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::pair quant_param = quantizationParams(-15.9375f, 15.9375f); - Tensor input_tensor = makeInputTensor( - input_shape, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S8, quant_param.first, quant_param.second); - Tensor scratchpad(DataType::S8, Shape({}), {}, ""); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 2; - params.stride_width = 2; - params.activation = Activation::RELU6; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - kernel.configure(); - _memory_manager->allocate_memory(scratchpad); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); -} + runtime_module.execute(); -TEST_F(AveragePool2DTest, Invalid_Input_Shape_NEG) -{ - Shape input_shape{1, 3, 5}; - std::vector input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - EXPECT_ANY_THROW(kernel.configure()); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(AveragePool2DTest, In_Out_Type_NEG) +TEST_F(AveragePool2DTest, Float_P) { - Shape input_shape{1, 3, 5, 1}; - std::vector input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - Tensor scratchpad(DataType::FLOAT32, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataFloatAveragePool2D test_data_kernel; + std::vector output_data_vector = checkAveragePool2DKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.001f)); } -TEST_F(AveragePool2DTest, Quant_Param_NEG) +TEST_F(AveragePool2DTest, InputOutputTypeMismatch_NEG) { - std::vector input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // - }; - - std::pair quant_param1 = quantizationParams(-15.9375f, 15.9375f); - std::pair quant_param2 = quantizationParams(-7.875f, 7.875f); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param1.first, quant_param1.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param2.first, quant_param2.second); - Tensor scratchpad(DataType::U8, Shape({}), {}, ""); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - params.activation = Activation::RELU6; - - AveragePool2D kernel(&input_tensor, &output_tensor, &scratchpad, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchAveragePool2DKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/BatchMatMul.h b/onert-micro/luci-interpreter/src/kernels/BatchMatMul.h index 84e93ed..744f497 100644 --- a/onert-micro/luci-interpreter/src/kernels/BatchMatMul.h +++ b/onert-micro/luci-interpreter/src/kernels/BatchMatMul.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp b/onert-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp index 90db77b..edfa3a6 100644 --- a/onert-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/BatchMatMul.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.h b/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.h index 803cf29..57703ea 100644 --- a/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.h +++ b/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp b/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp index fc936d1..52647a7 100644 --- a/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/BinaryOpCommon.h b/onert-micro/luci-interpreter/src/kernels/BinaryOpCommon.h index 2d2842a..1fd27ea 100644 --- a/onert-micro/luci-interpreter/src/kernels/BinaryOpCommon.h +++ b/onert-micro/luci-interpreter/src/kernels/BinaryOpCommon.h @@ -18,55 +18,197 @@ #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" +#include "TISOKernel.h" +#include "ProcessBroadcastShapes.h" + +#include "Utils.h" namespace luci_interpreter { namespace kernels { -// Derived from tensorflow/lite/kernels/internal/reference/maximum_minimum.h (v2.3.0). -template -void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape, - const T *input1_data, - const tflite::RuntimeShape &unextended_input2_shape, - const T *input2_data, - const tflite::RuntimeShape &unextended_output_shape, T *output_data, - Op op) +namespace +{ + +/** + * Fills activation min and max parameters depending on given data type and activation + * + * T is a template parameter, so after optimization this code left with only required if case + * + * @tparam T data type of arithmetic operation output tensor + * @param params tflite params to fill + * @param activation luci_interpreter::Activation of arithmetic operation + */ +template +void fillArithmeticActivationRange(luci_interpreter_pal::ArithmeticParams &p, Activation act) +{ + static_assert(one_of_types(), "Unsupported dtype"); + + if (std::is_same::value) + calculateActivationRange(act, &p.float_activation_min, &p.float_activation_max); + if (std::is_same::value) + calculateActivationRange(act, &p.quantized_activation_min, &p.quantized_activation_max); + else + calculateActivationRange(act, &p.int64_activation_min, &p.int64_activation_max); +} + +} // namespace + +template +void evalTISOKernel(TISOFunc tiso_func, TISOBroadcastFunc tiso_broadcast_func, + kernels::TISOKernel *kernel, kernels::TISOData *kernel_data, + const Options *options, RuntimeShape &&input_shape_1, + RuntimeShape &&input_shape_2) +{ + const auto *output = kernel->output(); + + luci_interpreter_pal::ArithmeticParams params{}; + fillArithmeticActivationRange(params, luci_actfunc(options->fused_activation_function())); + + const bool need_broadcast = + luci_interpreter_pal::ProcessBroadcastShapes(input_shape_1, input_shape_2, ¶ms); + + if (need_broadcast) + { + tiso_broadcast_func(params, input_shape_1, kernels::getTensorData(kernel_data->input1_data), + input_shape_2, kernels::getTensorData(kernel_data->input2_data), + kernels::getTensorShape(output), + kernels::getTensorData(kernel_data->output_data)); + } + else + { + const int flat_size = input_shape_1.flatSize(); + tiso_func(params, flat_size, kernels::getTensorData(kernel_data->input1_data), + kernels::getTensorData(kernel_data->input2_data), + kernels::getTensorData(kernel_data->output_data)); + } +} + +template +void evalTISOInplaceKernel(TISOFunc tiso_func, TISOBroadcastFunc tiso_broadcast_func, + kernels::TISOKernel *kernel, const Options *options, + RuntimeShape &&input_shape_1, RuntimeShape &&input_shape_2) +{ + uint8_t *inplace_data_ptr = nullptr; + circle::Tensor *input_inplace_tensor = nullptr; + + kernels::TISOData kernel_data = kernel->readInplaceData(inplace_data_ptr, input_inplace_tensor); + + evalTISOKernel( + tiso_func, tiso_broadcast_func, kernel, &kernel_data, options, std::move(input_shape_1), + std::move(input_shape_2)); + + BaseRuntimeGraph *runtime_graph = kernel->runtime_graph(); + + runtime_graph->makeInplaceOperation(input_inplace_tensor, kernel->output()); + if (input_inplace_tensor == kernel->input1()) + { + runtime_graph->makeInplaceOperation(kernel->input2(), nullptr); + } + else + { + runtime_graph->makeInplaceOperation(kernel->input1(), nullptr); + } +} + +#ifndef DIS_QUANT +template +void evalTISOQuantizedKernel(TISOFunc tiso_func, TISOBroadcastFunc tiso_broadcast_func, + kernels::TISOKernel *kernel, kernels::TISOData *kernel_data, + const Options *options) +{ + const auto *input1 = kernel->input1(); + const auto *input2 = kernel->input2(); + const auto *output = kernel->output(); + + const auto input1_scale = static_cast(Tensor::scale(input1)); + const auto input2_scale = static_cast(Tensor::scale(input2)); + const auto output_scale = static_cast(Tensor::scale(output)); + + 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{}; + kernels::quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, + &input1_shift); + kernels::quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, + &input2_shift); + kernels::quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, + &output_shift); + + int32_t activation_min{}; + int32_t activation_max{}; + kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), + output, &activation_min, &activation_max); + + luci_interpreter_pal::ArithmeticParams params{}; + params.left_shift = left_shift; + // The kernel expects inputs' zero points to be negated. + params.input1_offset = -Tensor::zero_point(input1); // Note the '-'. + params.input1_multiplier = input1_multiplier; + params.input1_shift = input1_shift; + params.input2_offset = -Tensor::zero_point(input2); // Note the '-'. + params.input2_multiplier = input2_multiplier; + params.input2_shift = input2_shift; + params.output_offset = Tensor::zero_point(output); + 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 = luci_interpreter_pal::ProcessBroadcastShapes( + kernels::getTensorShape(input1), kernels::getTensorShape(input2), ¶ms); + + if (need_broadcast) + { + tiso_broadcast_func( + params, kernels::getTensorShape(input1), kernels::getTensorData(kernel_data->input1_data), + kernels::getTensorShape(input2), kernels::getTensorData(kernel_data->input2_data), + kernels::getTensorShape(output), kernels::getTensorData(kernel_data->output_data)); + } + else + { + tiso_func(params, kernels::getTensorShape(input1), + kernels::getTensorData(kernel_data->input1_data), + kernels::getTensorShape(input2), kernels::getTensorData(kernel_data->input2_data), + kernels::getTensorShape(output), kernels::getTensorData(kernel_data->output_data)); + } +} + +template +void evalTISOInplaceQuantizedKernel(TISOFunc tiso_func, TISOBroadcastFunc tiso_broadcast_func, + kernels::TISOKernel *kernel, const Options *options) { - if (unextended_input1_shape == unextended_input2_shape) + uint8_t *inplace_data_ptr = nullptr; + circle::Tensor *input_inplace_tensor = nullptr; + + kernels::TISOData kernel_data = kernel->readInplaceData(inplace_data_ptr, input_inplace_tensor); + + evalTISOQuantizedKernel(tiso_func, tiso_broadcast_func, + kernel, &kernel_data, options); + + kernel->runtime_graph()->makeInplaceOperation(input_inplace_tensor, kernel->output()); + if (input_inplace_tensor == kernel->input1()) { - 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]); - } + kernel->runtime_graph()->makeInplaceOperation(kernel->input2(), nullptr); } else { - assert(unextended_input1_shape.DimensionsCount() <= N); - assert(unextended_input2_shape.DimensionsCount() <= N); - assert(unextended_output_shape.DimensionsCount() <= N); - - tflite::NdArrayDesc desc1{}; - tflite::NdArrayDesc desc2{}; - tflite::NdArrayDesc output_desc{}; - tflite::NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape, - &desc1, &desc2); - tflite::CopyDimsToDesc(tflite::RuntimeShape::ExtendedShape(N, unextended_output_shape), - &output_desc); - - auto fn = [&](int indexes[N]) { - output_data[SubscriptToIndex(output_desc, indexes)] = - op(input1_data[SubscriptToIndex(desc1, indexes)], - input2_data[SubscriptToIndex(desc2, indexes)]); - }; - tflite::NDOpsHelper(output_desc, fn); + kernel->runtime_graph()->makeInplaceOperation(kernel->input1(), nullptr); } } +#endif // DIS_QUANT + } // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Builders.h b/onert-micro/luci-interpreter/src/kernels/Builders.h index 13646a3..a49f71e 100644 --- a/onert-micro/luci-interpreter/src/kernels/Builders.h +++ b/onert-micro/luci-interpreter/src/kernels/Builders.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2023 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. @@ -18,19 +17,26 @@ #ifndef LUCI_INTERPRETER_KERNELS_NODES_BUILDERS_H #define LUCI_INTERPRETER_KERNELS_NODES_BUILDERS_H -#include "KernelBuilder.h" #include "luci_interpreter/core/reader/CircleMicroReader.h" -#include "core/RuntimeGraph.h" +#include "core/RuntimeModule.h" namespace luci_interpreter { +namespace +{ +#ifdef USE_STATIC_ALLOC +using BaseRuntimeGraph = StaticRuntimeGraph; +#else +using BaseRuntimeGraph = RuntimeGraph; +#endif // USE_STATIC_ALLOC +} // namespace + #define REGISTER_KERNEL(builtin_operator, name) \ void configure_kernel_Circle##name(const circle::Operator *cur_op, \ BaseRuntimeGraph *runtime_graph); \ \ - void execute_kernel_Circle##name(const circle::Operator *cur_op, \ - BaseRuntimeGraph *runtime_graph, bool is_inplace); + void execute_kernel_Circle##name(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph); #if USE_GENERATED_LIST #include "GeneratedKernelsToBuild.lst" diff --git a/onert-micro/luci-interpreter/src/kernels/CMakeLists.txt b/onert-micro/luci-interpreter/src/kernels/CMakeLists.txt index f59aa90..66c0a9e 100644 --- a/onert-micro/luci-interpreter/src/kernels/CMakeLists.txt +++ b/onert-micro/luci-interpreter/src/kernels/CMakeLists.txt @@ -1,11 +1,14 @@ set(SOURCES BinaryOpCommon.h - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. Utils.h Utils.cpp Builders.h KernelBuilder.h - KernelBuilder.cpp) + KernelBuilder.cpp + SISOKernel.h + TISOKernel.h + MISOKernel.h + PadCommon.cpp) macro(REGISTER_KERNEL OPERATOR, NODE) list(APPEND SOURCES "${NODE}.cpp") @@ -21,6 +24,7 @@ target_include_directories(${LUCI_INTERPRETER_KERNELS} PUBLIC ${LUCI_INTERPRETER target_link_libraries(${LUCI_INTERPRETER_KERNELS} PUBLIC ${LUCI_INTERPRETER_CORE}) +target_include_directories(${LUCI_INTERPRETER_KERNELS} PUBLIC ${LUCI_INTERPRETER_PAL_COMMON_DIR}) add_pal_to_target(${LUCI_INTERPRETER_KERNELS}) if(NOT ENABLE_TEST) @@ -38,4 +42,6 @@ include(${KERNEL_REGISTER_FILE}) list(APPEND TEST_SOURCES TestUtils.h TestUtils.cpp) GTest_AddTest(${LUCI_INTERPRETER_KERNELS}_test ${TEST_SOURCES}) -target_link_libraries(${LUCI_INTERPRETER_KERNELS}_test ${LUCI_INTERPRETER_KERNELS}) +target_link_libraries(${LUCI_INTERPRETER_KERNELS}_test onert_micro_coverage) +target_link_libraries(${LUCI_INTERPRETER_KERNELS} PUBLIC onert_micro_coverage) +target_link_libraries(${LUCI_INTERPRETER_KERNELS}_test ${LUCI_INTERPRETER_LOADER}) diff --git a/onert-micro/luci-interpreter/src/kernels/Cast.h b/onert-micro/luci-interpreter/src/kernels/Cast.h index 9249ccc..f0bd020 100644 --- a/onert-micro/luci-interpreter/src/kernels/Cast.h +++ b/onert-micro/luci-interpreter/src/kernels/Cast.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Cast.test.cpp b/onert-micro/luci-interpreter/src/kernels/Cast.test.cpp index 6a463d2..4713ad3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Cast.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Cast.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Concatenation.cpp b/onert-micro/luci-interpreter/src/kernels/Concatenation.cpp index a9eff09..c8c096e 100644 --- a/onert-micro/luci-interpreter/src/kernels/Concatenation.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Concatenation.cpp @@ -18,7 +18,7 @@ #include "Builders.h" #include "kernels/Utils.h" -#include +#include "PALConcatenation.h" namespace luci_interpreter { @@ -27,7 +27,7 @@ namespace { template -void evalGeneric(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, bool) +void evalGeneric(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { const auto output_index = cur_op->outputs()->operator[](0); @@ -44,37 +44,38 @@ void evalGeneric(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph const auto input_sizes = cur_op->inputs()->size(); std::vector all_input_data; - std::vector all_shape; - std::vector all_shape_ptr; - - all_input_data.reserve(input_sizes); - all_shape.reserve(input_sizes); - all_shape_ptr.reserve(input_sizes); + std::vector all_shape; + std::vector all_shape_ptr; for (int32_t i = 0; i < input_sizes; ++i) { auto input_index = cur_op->inputs()->operator[](i); const auto *tensor = runtime_graph->getCircleTensorByIndex(input_index); - auto *data = reinterpret_cast(runtime_graph->getDataByTensor(tensor)); + const auto *tensor_data = runtime_graph->getDataByTensor(tensor); + if (tensor_data == nullptr) + tensor_data = runtime_graph->getConstDataByTensor(tensor); + + auto *data = reinterpret_cast(tensor_data); + + auto runtime_shape = kernels::getTensorRuntimeShape(tensor, runtime_graph); all_input_data.push_back(data); - all_shape.push_back(kernels::getTensorShape(tensor)); + all_shape.push_back(runtime_shape); } - for (tflite::RuntimeShape &shape : all_shape) + for (luci_interpreter::RuntimeShape &shape : all_shape) { all_shape_ptr.push_back(&shape); } auto *output_data = reinterpret_cast(runtime_graph->getDataByTensor(output)); - // kernels::VectorOfTensors inputs(_inputs); - tflite::ConcatenationParams params{}; + luci_interpreter_pal::ConcatenationParams params{}; params.axis = axis; - params.inputs_count = input_sizes; - tflite::reference_ops::Concatenation(params, all_shape_ptr.data(), all_input_data.data(), - kernels::getTensorShape(output), output_data); + params.inputs_count = all_shape.size(); + luci_interpreter_pal::Concatenation(params, all_shape_ptr.data(), all_input_data.data(), + kernels::getTensorShape(output), output_data); } } // namespace @@ -104,24 +105,12 @@ void configure_kernel_CircleConcatenation(const circle::Operator *cur_op, axis += Tensor::num_dims(t0); LUCI_INTERPRETER_CHECK(axis >= 0 && axis < Tensor::num_dims(t0)); - int32_t sum_axis = Tensor::dim(t0, axis); for (int i = 1; i < num_inputs; ++i) { input_index = cur_op->inputs()->operator[](i); const auto *tensor = runtime_graph->getCircleTensorByIndex(input_index); LUCI_INTERPRETER_CHECK(Tensor::element_type(tensor) == Tensor::element_type(t0)); LUCI_INTERPRETER_CHECK(Tensor::num_dims(tensor) == Tensor::num_dims(t0)); - for (int d = 0; d < Tensor::num_dims(t0); ++d) - { - if (d == axis) - { - sum_axis += Tensor::dim(tensor, axis); - } - else - { - LUCI_INTERPRETER_CHECK(Tensor::dim(tensor, d) == Tensor::dim(t0, d)); - } - } } #ifndef DIS_QUANT @@ -145,7 +134,7 @@ void configure_kernel_CircleConcatenation(const circle::Operator *cur_op, } void execute_kernel_CircleConcatenation(const circle::Operator *cur_op, - BaseRuntimeGraph *runtime_graph, bool is_inplace) + BaseRuntimeGraph *runtime_graph) { int num_inputs = cur_op->inputs()->size(); LUCI_INTERPRETER_CHECK(num_inputs > 0); @@ -158,20 +147,20 @@ void execute_kernel_CircleConcatenation(const circle::Operator *cur_op, { #ifndef DIS_FLOAT case DataType::FLOAT32: - evalGeneric(cur_op, runtime_graph, is_inplace); + evalGeneric(cur_op, runtime_graph); break; #endif // DIS_FLOAT #ifndef DIS_QUANT case DataType::S8: - evalGeneric(cur_op, runtime_graph, is_inplace); + evalGeneric(cur_op, runtime_graph); break; +#endif // DIS_QUANT case DataType::S32: - evalGeneric(cur_op, runtime_graph, is_inplace); + evalGeneric(cur_op, runtime_graph); break; case DataType::S64: - evalGeneric(cur_op, runtime_graph, is_inplace); + evalGeneric(cur_op, runtime_graph); break; -#endif default: assert(false && "Unsupported type."); } diff --git a/onert-micro/luci-interpreter/src/kernels/Concatenation.test.cpp b/onert-micro/luci-interpreter/src/kernels/Concatenation.test.cpp index 25234c5..6f00941 100644 --- a/onert-micro/luci-interpreter/src/kernels/Concatenation.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Concatenation.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -14,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/Concatenation.h" + #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/concatenation/FloatConcatenationKernel.h" +#include "luci_interpreter/test_models/concatenation/IntConcatenationKernel.h" +#include "luci_interpreter/test_models/concatenation/NegConcatenationKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -31,241 +30,106 @@ using namespace testing; class ConcatenationTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(ConcatenationTest, Float) +template +std::vector checkConcatenationKernel(test_kernel::TestDataBase *test_data_base) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({2, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - // Try different 'axis' and expect different results. - { - params.axis = 0; - params.activation = luci::FusedActFunc::NONE; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - kernel.configure(); - for (auto t : kernel.getOutputTensors()) - { - _memory_manager->allocate_memory(*t); - } - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - EXPECT_THAT(extractTensorData(output_tensor), - FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); - } + // set left input data { - params.axis = -2; // Same as '0'. - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - { - params.axis = 1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - EXPECT_THAT(extractTensorData(output_tensor), - FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})); - } + // set right input data { - params.axis = -1; // Same as '1'. - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12})); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); } -} -TEST_F(ConcatenationTest, Input_Number_Check_NEG) -{ - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; + runtime_module.execute(); - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - Concatenation kernel({}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(ConcatenationTest, Invalid_Axis_NEG) +TEST_F(ConcatenationTest, Float_P) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({2, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; - - params.axis = -3; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataFloatConcatenation test_data_kernel; + std::vector output_data_vector = checkConcatenationKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(ConcatenationTest, Mismatching_Input_Type_NEG) +TEST_F(ConcatenationTest, Int32_P) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({2, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataS32Concatenation test_data_kernel; + std::vector output_data_vector = checkConcatenationKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(ConcatenationTest, Mismatching_Input_Dimension_Num_NEG) +TEST_F(ConcatenationTest, Int64_P) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({1, 2, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataS64Concatenation test_data_kernel; + std::vector output_data_vector = checkConcatenationKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(ConcatenationTest, Mismatching_Input_Dimension_NEG) +TEST_F(ConcatenationTest, InputTypeMismatch_NEG) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12, 13, 14, 15}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({3, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataInputTypeMismatchConcatenation test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(ConcatenationTest, Int8_Mismatching_Input_Type_NEG) +TEST_F(ConcatenationTest, InputOutputTypeMismatch_NEG) { - std::vector input1_data{1, 2, 3, 4}; - std::vector input2_data{5, 6, 7, 8}; - Tensor input1_tensor = makeInputTensor({2, 2}, input1_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({2, 2}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S8); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataReluConcatenation test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(ConcatenationTest, Int8_Mismatching_Input_Output_Quant_Params_NEG) +TEST_F(ConcatenationTest, WrongAxis_NEG) { - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - int quantized_dimension = 3; - std::vector scales{0.1, 0.2, 0.3}; - std::vector zero_points{1, -1, 1}; - - Tensor input1_tensor = makeInputTensor( - {1, 1, 2, 3}, scales, zero_points, quantized_dimension, input1_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - {1, 1, 2, 3}, scales, zero_points, quantized_dimension, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S8, scales.at(0), zero_points.at(0)); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataWrongAxisConcatenation test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(ConcatenationTest, Int8_Mismatching_Zero_Point_NEG) -{ - std::vector input1_data{1, 2, 3, 4}; - std::vector input2_data{5, 6, 7, 8}; - float scale = 0.1; - int32_t zero_point_1 = 1; - int32_t zero_point_2 = -1; - - Tensor input1_tensor = - makeInputTensor({2, 2}, scale, zero_point_1, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({2, 2}, scale, zero_point_2, input2_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::S8, scale, zero_point_1); - ConcatenationParams params{}; - - params.axis = -1; - params.activation = luci::FusedActFunc::NONE; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} - -// TODO: Remove this test when concat w/ fused_activation is supported -TEST_F(ConcatenationTest, With_Fused_Activation_NEG) -{ - std::vector input1_data{1, 2, 3, 4, 5, 6}; - std::vector input2_data{7, 8, 9, 10, 11, 12}; - Tensor input1_tensor = - makeInputTensor({2, 3}, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({2, 3}, input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - ConcatenationParams params{}; - - params.axis = 1; - params.activation = luci::FusedActFunc::RELU; - - Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add tests for S8 } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/Conv2D.cpp b/onert-micro/luci-interpreter/src/kernels/Conv2D.cpp index 7045249..75bccbc 100644 --- a/onert-micro/luci-interpreter/src/kernels/Conv2D.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Conv2D.cpp @@ -66,7 +66,7 @@ void evalFloat(const circle::Tensor *input, const circle::Tensor *filter, kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), &activation_min, &activation_max); - tflite::ConvParams params{}; + luci_interpreter_pal::ConvParams params{}; params.padding_values.height = compute_padding_h(input, filter, options); params.padding_values.width = compute_padding_w(input, filter, options); params.stride_height = options->stride_h(); @@ -82,12 +82,19 @@ void evalFloat(const circle::Tensor *input, const circle::Tensor *filter, auto *filter_data = runtime_graph->getConstDataByTensor(filter); auto *bias_data = runtime_graph->getConstDataByTensor(bias); - luci_interpreter_pal::Conv( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(filter), kernels::getTensorData(filter_data), - kernels::getTensorShape(bias), kernels::getTensorData(bias_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data), - kernels::getTensorShape(nullptr), nullptr); + int32_t input_shape[kMaxSmallSize]; + kernels::getTensorDims(input, runtime_graph, input_shape); + + int32_t filter_shape[kMaxSmallSize]; + kernels::getTensorDims(filter, runtime_graph, filter_shape); + + int32_t output_shape[kMaxSmallSize]; + kernels::getTensorDims(output, runtime_graph, output_shape); + + luci_interpreter_pal::Conv(params, input_shape, kernels::getTensorData(input_data), + filter_shape, kernels::getTensorData(filter_data), + kernels::getTensorData(bias_data), output_shape, + kernels::getTensorData(output_data)); } #endif // DIS_FLOAT @@ -112,7 +119,7 @@ void evalQuantized(const circle::Tensor *input, const circle::Tensor *filter, kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), output, &activation_min, &activation_max); - tflite::ConvParams params{}; + luci_interpreter_pal::ConvParams params{}; params.padding_values.height = compute_padding_h(input, filter, options); params.padding_values.width = compute_padding_w(input, filter, options); params.stride_height = options->stride_h(); @@ -134,12 +141,19 @@ void evalQuantized(const circle::Tensor *input, const circle::Tensor *filter, auto *filter_data = runtime_graph->getConstDataByTensor(filter); auto *bias_data = runtime_graph->getConstDataByTensor(bias); - luci_interpreter_pal::Conv( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(filter), kernels::getTensorData(filter_data), - kernels::getTensorShape(bias), kernels::getTensorData(bias_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data), - kernels::getTensorShape(nullptr), nullptr); + int32_t input_shape[kMaxSmallSize]; + kernels::getTensorDims(input, runtime_graph, input_shape); + + int32_t filter_shape[kMaxSmallSize]; + kernels::getTensorDims(filter, runtime_graph, filter_shape); + + int32_t output_shape[kMaxSmallSize]; + kernels::getTensorDims(output, runtime_graph, output_shape); + + luci_interpreter_pal::Conv(params, input_shape, kernels::getTensorData(input_data), + filter_shape, kernels::getTensorData(filter_data), + kernels::getTensorData(bias_data), output_shape, + kernels::getTensorData(output_data)); } void evalQuantizedPerChannel(const circle::Tensor *input, const circle::Tensor *filter, @@ -223,7 +237,7 @@ void evalQuantizedPerChannel(const circle::Tensor *input, const circle::Tensor * acc += bias_data[out_c]; } - int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( + int32_t scaled_acc = luci_interpreter_pal::multiplyByQuantizedMultiplier( acc, quant_multipliers[out_c].multiplier, quant_multipliers[out_c].shift); scaled_acc += Tensor::zero_point(output); @@ -235,150 +249,6 @@ void evalQuantizedPerChannel(const circle::Tensor *input, const circle::Tensor * } } } - -void evalQuantizedS8PerChannel(const circle::Tensor *input, const circle::Tensor *filter, - const circle::Tensor *bias, const circle::Tensor *output, - const circle::Conv2DOptions *options, - BaseRuntimeGraph *runtime_graph) -{ - int32_t activation_min{}; - int32_t activation_max{}; - kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), - output, &activation_min, &activation_max); - - tflite::ConvParams params{}; - params.padding_values.height = compute_padding_h(input, filter, options); - params.padding_values.width = compute_padding_w(input, filter, options); - params.stride_height = options->stride_h(); - params.stride_width = options->stride_w(); - params.dilation_height_factor = options->dilation_h_factor(); - params.dilation_width_factor = options->dilation_w_factor(); - // The kernel expects filter zero points to be negated. - params.input_offset = -Tensor::zero_point(input); // Note the '-'. - params.weights_offset = 0; // Unused in tflite code - params.output_offset = Tensor::zero_point(output); - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - const std::vector effective_output_scales = kernels::getQuantizedConvolutionMultiplers( - Tensor::scale(input), Tensor::scales(filter), Tensor::scale(output)); - - std::vector quant_multipliers = - kernels::quantizeMultipliers(effective_output_scales); - - std::vector shifts; - std::transform(quant_multipliers.begin(), quant_multipliers.end(), std::back_inserter(shifts), - [](kernels::ChannelQuantMultipliers cm) { return cm.shift; }); - std::vector multipliers; - std::transform(quant_multipliers.begin(), quant_multipliers.end(), - std::back_inserter(multipliers), - [](kernels::ChannelQuantMultipliers cm) { return cm.multiplier; }); - - auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); - - auto *filter_data = runtime_graph->getConstDataByTensor(filter); - auto *bias_data = runtime_graph->getConstDataByTensor(bias); - - luci_interpreter_pal::ConvPerChannel( - params, multipliers.data(), shifts.data(), kernels::getTensorShape(input), - kernels::getTensorData(input_data), kernels::getTensorShape(filter), - kernels::getTensorData(filter_data), kernels::getTensorShape(bias), - kernels::getTensorData(bias_data), kernels::getTensorShape(output), - kernels::getTensorData(output_data), kernels::getTensorShape(nullptr), nullptr); -} - -void evalQuantizedS16(const circle::Tensor *input, const circle::Tensor *filter, - const circle::Tensor *bias, const circle::Tensor *output, - const circle::Conv2DOptions *options, BaseRuntimeGraph *runtime_graph) -{ - auto *raw_input_data = runtime_graph->getDataByTensor(input); - auto *raw_output_data = runtime_graph->getDataByTensor(output); - - auto *raw_filter_data = runtime_graph->getConstDataByTensor(filter); - auto *raw_bias_data = runtime_graph->getConstDataByTensor(bias); - - const auto *input_data = kernels::getTensorData(raw_input_data); - const auto *filter_data = kernels::getTensorData(raw_filter_data); - const auto *bias_data = kernels::getTensorData(raw_bias_data); - auto *output_data = kernels::getTensorData(raw_output_data); - - const int32_t batches = Tensor::dim(input, 0); - const int32_t input_height = Tensor::dim(input, 1); - const int32_t input_width = Tensor::dim(input, 2); - const int32_t input_depth = Tensor::dim(input, 3); - const int32_t output_depth = Tensor::dim(filter, 0); - const int32_t filter_height = Tensor::dim(filter, 1); - const int32_t filter_width = Tensor::dim(filter, 2); - const int32_t output_height = Tensor::dim(output, 1); - const int32_t output_width = Tensor::dim(output, 2); - - const int32_t stride_height = options->stride_h(); - const int32_t stride_width = options->stride_w(); - const int32_t dilation_height_factor = options->dilation_h_factor(); - const int32_t dilation_width_factor = options->dilation_w_factor(); - - int32_t activation_min{}; - int32_t activation_max{}; - kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), - output, &activation_min, &activation_max); - - const std::vector effective_output_scale = kernels::getQuantizedConvolutionMultiplers( - Tensor::scale(input), Tensor::scales(filter), Tensor::scale(output)); - - const std::vector multipliers_raw = - kernels::quantizeMultipliers(effective_output_scale); - kernels::BroadcastableWrapper multipliers(multipliers_raw); - - for (int32_t batch = 0; batch < batches; ++batch) - { - for (int32_t out_y = 0; out_y < output_height; ++out_y) - { - for (int32_t out_x = 0; out_x < output_width; ++out_x) - { - for (int32_t out_c = 0; out_c < output_depth; ++out_c) - { - const int32_t in_y_origin = - out_y * stride_height - compute_padding_h(input, filter, options); - const int32_t in_x_origin = - out_x * stride_width - compute_padding_w(input, filter, options); - int64_t acc = 0; - for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y) - { - for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x) - { - const int32_t in_y = in_y_origin + dilation_height_factor * filter_y; - const int32_t in_x = in_x_origin + dilation_width_factor * filter_x; - if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) - { - for (int32_t in_c = 0; in_c < input_depth; ++in_c) - { - const int16_t input_val = - input_data[kernels::calcOffset(input, batch, in_y, in_x, in_c)]; - const int16_t filter_val = - filter_data[kernels::calcOffset(filter, out_c, filter_y, filter_x, in_c)]; - acc += static_cast(input_val) * static_cast(filter_val); - } - } - } - } - if (bias_data) - { - acc += bias_data[out_c]; - } - - int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( - acc, multipliers[out_c].multiplier, multipliers[out_c].shift); - - scaled_acc = std::max(scaled_acc, activation_min); - scaled_acc = std::min(scaled_acc, activation_max); - - output_data[kernels::calcOffset(output, batch, out_y, out_x, out_c)] = scaled_acc; - } - } - } - } -} #endif // DIS_QUANT } // namespace @@ -462,8 +332,7 @@ void configure_kernel_CircleConv2D(const circle::Operator *cur_op, BaseRuntimeGr } } -void execute_kernel_CircleConv2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, - bool) +void execute_kernel_CircleConv2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); const auto weight_index = cur_op->inputs()->operator[](1); @@ -509,12 +378,6 @@ void execute_kernel_CircleConv2D(const circle::Operator *cur_op, BaseRuntimeGrap evalQuantizedPerChannel(input, weights, bias, output, options, runtime_graph); } break; - case DataType::S8: - evalQuantizedS8PerChannel(input, weights, bias, output, options, runtime_graph); - break; - case DataType::S16: - evalQuantizedS16(input, weights, bias, output, options, runtime_graph); - break; #endif // DIS_QUANT default: assert(false && "Unsupported type."); diff --git a/onert-micro/luci-interpreter/src/kernels/Conv2D.test.cpp b/onert-micro/luci-interpreter/src/kernels/Conv2D.test.cpp index 6c6d860..c373bd2 100644 --- a/onert-micro/luci-interpreter/src/kernels/Conv2D.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Conv2D.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,16 +14,15 @@ * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/Conv2D.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/conv2d/FloatConv2DKernel.h" +#include "luci_interpreter/test_models/conv2d/U8Conv2DKernel.h" +#include "luci_interpreter/test_models/conv2d/NegConv2DKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -32,680 +30,92 @@ using namespace testing; class Conv2DTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(Conv2DTest, Float) -{ - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(im2col); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 11, 16, 7, 20, // row = 0 - 0, 40, 0, 44, // row = 1 - }; - std::vector ref_output_shape{1, 2, 2, 2}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, FloatPointwise) -{ - Shape input_shape{1, 2, 2, 2}; - Shape filter_shape{2, 1, 1, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, // row = 0, col = 0 - 3, 4, // row = 0, col = 1 - 5, 6, // row = 1, col = 0 - 7, 8, // row = 1, col = 1 - }; - std::vector filter_data{ - -1, 2, // out = 0 - -3, 4, // out = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 1; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(im2col); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 4, 7, 6, 9, // row = 0 - 8, 11, 10, 13, // row = 1 - }; - std::vector ref_output_shape{1, 2, 2, 2}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, FloatCheck) -{ - Shape input_shape{2, 2, 4, 1}; - Shape filter_shape{3, 2, 2, 1}; - Shape bias_shape{3}; - std::vector input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 - }; - std::vector filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter - }; - std::vector bias_data{1, 2, 3}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 2; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::NONE; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - std::vector ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right - }; - std::vector ref_output_shape{2, 1, 2, 3}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, Uint8) -{ - std::vector input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 - }; - std::vector filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter - }; - std::vector bias_data{1, 2, 3}; - - std::pair input_quant_param = quantizationParams(-63.5, 64); - std::pair output_quant_param = quantizationParams(-127, 128); - - Tensor input_tensor = - makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, - input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor({3, 2, 2, 1}, input_quant_param.first, input_quant_param.second, - filter_data, _memory_manager.get()); - Tensor bias_tensor = makeInputTensor( - {3}, input_quant_param.first * input_quant_param.first, 0, bias_data, _memory_manager.get()); - Tensor im2col(DataType::U8, Shape({}), {}, ""); - Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 2; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::NONE; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - std::vector ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right - }; - std::vector ref_output_shape{2, 1, 2, 3}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, Uint8_CWQ) -{ - const int output_channels = 3; - std::vector input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 - }; - std::vector filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter - }; - std::vector bias_data{1, 2, 3}; - Shape filter_shape{output_channels, 2, 2, 1}; - - std::pair input_quant_param = quantizationParams(0, 4); - std::pair output_quant_param = quantizationParams(-127, 128); - - std::vector> filter_quant_params; - filter_quant_params.push_back(quantizationParams(0, 4)); - filter_quant_params.push_back(quantizationParams(-1, 1)); - filter_quant_params.push_back(quantizationParams(-1, 1)); - - std::vector filter_scales; - std::vector filter_zerops; - for (auto iter : filter_quant_params) - { - filter_scales.push_back(iter.first); - filter_zerops.push_back(iter.second); - } - - std::vector bias_scales; - for (int i = 0; i < output_channels; ++i) - bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); - std::vector zerop(output_channels, 0); - - Tensor input_tensor = - makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, - input_data, _memory_manager.get()); - Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, - 0, filter_data, _memory_manager.get()); - Tensor bias_tensor = makeInputTensor({output_channels}, bias_scales, zerop, 0, - bias_data, _memory_manager.get()); - Tensor im2col(DataType::U8, Shape({}), {}, ""); - Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 2; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::NONE; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - std::vector ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right - }; - std::vector ref_output_shape{2, 1, 2, 3}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, SInt8_CWQ) +template std::vector checkConv2DKernel(test_kernel::TestDataBase *test_data_base) { - const int output_channels = 3; - std::vector input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 - }; - std::vector filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter - }; - std::vector bias_data{1, 2, 3}; - Shape filter_shape{output_channels, 2, 2, 1}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::pair input_quant_param = quantizationParams(0, 4); - std::pair output_quant_param = quantizationParams(-127, 128); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - std::vector> filter_quant_params; - filter_quant_params.push_back(std::pair(0.5, 0)); - filter_quant_params.push_back(std::pair(0.25, 0)); - filter_quant_params.push_back(std::pair(0.125, 0)); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - std::vector filter_scales; - std::vector filter_zerops; - for (auto iter : filter_quant_params) + // set input data { - filter_scales.push_back(iter.first); - filter_zerops.push_back(iter.second); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - std::vector bias_scales; - for (int i = 0; i < output_channels; ++i) - bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first); - std::vector zerop(output_channels, 0); - - Tensor input_tensor = - makeInputTensor({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, - input_data, _memory_manager.get()); - Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, filter_zerops, - 0, filter_data, _memory_manager.get()); - Tensor bias_tensor = makeInputTensor({output_channels}, bias_scales, zerop, 0, - bias_data, _memory_manager.get()); - Tensor im2col(DataType::S8, Shape({}), {}, ""); - Tensor output_tensor = - makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 2; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::NONE; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - std::vector ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right - }; - std::vector ref_output_shape{2, 1, 2, 3}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -TEST_F(Conv2DTest, SInt16) -{ - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector ref_output_shape{1, 2, 2, 2}; - - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - std::vector ref_output_data{ - 11, 16, 7, 20, // row = 0 - 0, 40, 0, 44, // row = 1 - }; - - Tensor input_tensor = - makeInputTensor(input_shape, 0.25, 0, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, 0.2, 0, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, 0.25 * 0.2, 0, bias_data, _memory_manager.get()); - Tensor im2col(DataType::S16, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); + runtime_module.execute(); - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(Conv2DTest, SInt16_CWQ_weights) +TEST_F(Conv2DTest, Float_P) { - Shape input_shape{1, 2, 2, 2}; // Batch x H x W x C - Shape filter_shape{3, 1, 1, 2}; // Out channels x H x W x In Channels - Shape bias_shape{3}; - std::vector ref_output_shape{1, 2, 2, 3}; - - std::vector input_data{ - 1, 2, // row = 0, col 0 - 3, 4, // row = 0, col 1 - 5, 6, // row = 1, col 0 - 7, 8, // row = 1, col 1 - }; - std::vector filter_data{ - 4, -3, // out = 0 - 1, -3, // out = 1 - 5, -3, // out = 2 - }; - std::vector bias_data{1, 10, 5}; - std::vector ref_output_data{ - 0, 5, 4, // row 0, col 0 - 1, 1, 8, // row 0, col 1 - 3, 0, 12, // row 1, col 0 - 5, 0, 16, // row 1, col 1 - }; - - float input_scale = 0.25f; - float output_scale = 0.05f; - std::vector filter_scales = {0.25f, 0.2f, 0.1f}; - std::vector bias_scales; - for (int i = 0; i < filter_scales.size(); ++i) - bias_scales.push_back(filter_scales[i] * input_scale); - std::vector zerop = {0, 0, 0}; - - Tensor input_tensor = - makeInputTensor(input_shape, input_scale, 0, input_data, _memory_manager.get()); - Tensor filter_tensor = makeInputTensor(filter_shape, filter_scales, zerop, 0, - filter_data, _memory_manager.get()); - Tensor bias_tensor = makeInputTensor(bias_shape, bias_scales, zerop, 0, bias_data, - _memory_manager.get()); - Tensor im2col(DataType::S16, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 1; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(im2col); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + test_kernel::TestDataFloatConv2D test_data_kernel; + std::vector output_data_vector = checkConv2DKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(Conv2DTest, Unsupported_Type_Configure_NEG) +TEST_F(Conv2DTest, U8_P) { - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataU8Conv2D test_data_kernel; + std::vector output_data_vector = checkConv2DKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(Conv2DTest, Invalid_Bias_Type_NEG) +TEST_F(Conv2DTest, Input_type_mismatch_NEG) { - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + test_kernel::NegTestDataInputMismatchConv2DKernel test_data_kernel; - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(Conv2DTest, Invalid_Bias_Data_NEG) -{ - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{3}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2, 3}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - EXPECT_ANY_THROW(kernel.configure()); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(Conv2DTest, Invalid_Input_Shape_NEG) +TEST_F(Conv2DTest, Wrong_bias_type_NEG) { - Shape input_shape{1, 4, 6, 1}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + test_kernel::NegTestDataWrongBiasTypeConv2DKernel test_data_kernel; - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::RELU; - - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - EXPECT_ANY_THROW(kernel.configure()); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(Conv2DTest, Invalid_fused_act_tanh_NEG) +TEST_F(Conv2DTest, Invalid_input_type_NEG) { - Shape input_shape{1, 4, 3, 2}; - Shape filter_shape{2, 2, 2, 2}; - Shape bias_shape{2}; - std::vector input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 - }; - std::vector filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 - }; - std::vector bias_data{1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor filter_tensor = - makeInputTensor(filter_shape, filter_data, _memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, _memory_manager.get()); - Tensor im2col(DataType::FLOAT32, Shape({}), {}, ""); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Conv2DParams params{}; - params.padding = Padding::VALID; - params.stride_height = 2; - params.stride_width = 1; - params.dilation_height_factor = 1; - params.dilation_width_factor = 1; - params.activation = Activation::TANH; + test_kernel::NegTestDataInvalidInputTypeConv2DKernel test_data_kernel; - Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, &im2col, params); - EXPECT_ANY_THROW(kernel.configure()); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.cpp b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.cpp index 28f04d1..937958b 100644 --- a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.cpp +++ b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.h b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.h index 42cf190..63ce376 100644 --- a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.h +++ b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp index f367c1b..88e6e07 100644 --- a/onert-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/DepthToSpace.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h b/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h index daa1505..3d1faf6 100644 --- a/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h +++ b/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp b/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp index a6e3182..6b4673f 100644 --- a/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Dequantize.cpp b/onert-micro/luci-interpreter/src/kernels/Dequantize.cpp index 5d4b17d..a097342 100644 --- a/onert-micro/luci-interpreter/src/kernels/Dequantize.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Dequantize.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Dequantize.h b/onert-micro/luci-interpreter/src/kernels/Dequantize.h index 5d671f3..5565df0 100644 --- a/onert-micro/luci-interpreter/src/kernels/Dequantize.h +++ b/onert-micro/luci-interpreter/src/kernels/Dequantize.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Div.cpp b/onert-micro/luci-interpreter/src/kernels/Div.cpp index d1cd7f7..f8a4690 100644 --- a/onert-micro/luci-interpreter/src/kernels/Div.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Div.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,140 +14,64 @@ * limitations under the License. */ -#include "kernels/Div.h" - +#include "Builders.h" #include "kernels/Utils.h" -#include -#include +#include "kernels/BinaryOpCommon.h" -namespace luci_interpreter -{ -namespace kernels -{ +#include "PALDiv.h" -Div::Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams ¶ms) - : KernelWithParams({input1, input2}, {output}, params) +namespace luci_interpreter { -} -void Div::configure() +// TODO: reduce code duplication with Mul +void configure_kernel_CircleDiv(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); - LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); + kernels::TISOKernel kernel(cur_op, runtime_graph); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); } -void Div::execute() const +void execute_kernel_CircleDiv(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (input1()->element_type()) - { - case DataType::FLOAT32: - evalFloat(); - break; - case DataType::S64: - evalInteger(); - break; - case DataType::S32: - evalInteger(); - break; - case DataType::U8: - evalQuantized(); - break; - default: - assert(false && "Unsupported type."); - } -} - -void Div::evalFloat() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); + kernels::TISOKernel kernel(cur_op, runtime_graph); - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - tflite::reference_ops::BroadcastDivSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} + const auto *options = cur_op->builtin_options_as_DivOptions(); -template void Div::evalInteger() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); + luci_interpreter::RuntimeShape input_shape1 = + kernels::getTensorRuntimeShape(kernel.input1(), runtime_graph); + luci_interpreter::RuntimeShape input_shape2 = + kernels::getTensorRuntimeShape(kernel.input2(), runtime_graph); - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + bool is_inplace = runtime_graph->is_inplace_op(cur_op); - if (need_broadcast) + switch (Tensor::element_type(kernel.input1())) { - tflite::reference_ops::BroadcastDivSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -void Div::evalQuantized() const -{ - const auto input1_scale = static_cast(input1()->scale()); - const auto input2_scale = static_cast(input2()->scale()); - const auto output_scale = static_cast(output()->scale()); - - const double real_output_multiplier = input1_scale / (input2_scale * output_scale); - - int32_t output_multiplier{}; - int output_shift{}; - - quantizeMultiplier(real_output_multiplier, &output_multiplier, &output_shift); - - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - tflite::ArithmeticParams params{}; - - params.input1_offset = -input1()->zero_point(); // Note the '-'. - params.input2_offset = -input2()->zero_point(); // Note the '-'. - params.output_offset = output()->zero_point(); - params.output_multiplier = output_multiplier; - params.output_shift = output_shift; - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - tflite::reference_ops::BroadcastDivSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + auto tiso_func = luci_interpreter_pal::Div; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastDiv4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Div.h b/onert-micro/luci-interpreter/src/kernels/Div.h deleted file mode 100644 index ac88661..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Div.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_DIV_H -#define LUCI_INTERPRETER_KERNELS_DIV_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Div : public KernelWithParams -{ -public: - Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams ¶ms); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_DIV_H diff --git a/onert-micro/luci-interpreter/src/kernels/Div.test.cpp b/onert-micro/luci-interpreter/src/kernels/Div.test.cpp index 85cd8b9..d601439 100644 --- a/onert-micro/luci-interpreter/src/kernels/Div.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Div.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/Div.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/div/FloatDivKernel.h" +#include "luci_interpreter/test_models/div/NegDivKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,201 +30,93 @@ using namespace testing; class DivTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -float GetTolerance(float min, float max) -{ - const float kQuantizedStep = (max - min) / 255.0f; - const float kQuantizedTolerance = 2.0f * kQuantizedStep + kQuantizedStep * kQuantizedStep; - return kQuantizedTolerance; -} - -TEST_F(DivTest, Float) -{ - Shape base_shape = {2, 3, 1, 1}; - - std::vector output_shape = {2, 3, 1, 1}; - - std::vector input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f}; - std::vector input2_data{0.2f, 1.6f, 0.5f, 0.4f, 1.6f, 0.4f}; - std::vector test_outputs{1.5f, 1.4375f, 1.8f, 1.25f, 0.5f, 2.75f}; - - Tensor input1_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(base_shape, input2_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); -} - -TEST_F(DivTest, FloatBroadcast) -{ - Shape input1_shape = {1, 3}; - Shape input2_shape = {3, 1}; - - std::vector input1_data{-0.3f, 2.3f, 0.9f}; - std::vector input2_data{0.2f, 1.6f, 0.5f}; - std::vector test_outputs{0.f, 11.5f, 4.5f, 0.f, 1.4375f, 0.5625f, 0.f, 4.6f, 1.8f}; - - Tensor input1_tensor = - makeInputTensor(input1_shape, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(input2_shape, input2_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs, 0.0001f)); -} - -TEST_F(DivTest, Uint8) +template std::vector checkDivKernel(test_kernel::TestDataBase *test_data_base) { - Shape base_shape = {1, 2, 2, 1}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector output_shape = {1, 2, 2, 1}; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - std::vector input1_data = {-0.8f, -0.2f, 0.3f, 0.7f}; - std::vector input2_data = {-0.8f, 0.4f, 0.8f, 1.0f}; - std::vector test_outputs{1.0f, 0.f, 0.375f, 0.7f}; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - const float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - - std::pair quant_param = quantizationParams(-1.f, 1.f); - - Tensor input1_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, input1_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, input2_data, _memory_manager.get()); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - Tensor output_tensor = - makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - DivParams params{}; - params.activation = Activation::RELU; + runtime_module.execute(); - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(test_outputs, kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template void checkInteger(luci_interpreter::IMemoryManager *memory_manager) +TEST_F(DivTest, Float_P) { - using dtype = typename loco::DataTypeImpl::Type; - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - - std::vector> test_outputs = {{5, 6, 2, 0, 10, 3, // - 10, 0, 4, 5, 20, 0, // - 0, 0, 0, 2, 0, 0, // - 2, 0, 1, 10, 5, 0, // - 2, 3, 1, 0, 5, 1, // - 18, 20, 7, 0, 37, 10}, - {5, 6, 4, 5, 0, 0, 2, 0, 1, 0, 37, 10}, - {5, 7, 4, 6, 2, 3, 10, 0, 8, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 10, 5, 0, 1, 0, - 0, 0, 5, 9, 1, 1, 0, 0, 37, 50, 7, 10}, - {5, 7, 8, 0, 0, 0, 0, 10, 5, 9, 7, 10}}; - std::vector input1_data{20, 30, 40, -17, -4, -7, 11, -31, 10, 19, 75, 100}; - std::vector input2_data{4, 5, 10, -3, 2, 10}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestDataFloatDiv test_data_kernel(is_with_broadcast); + std::vector output_data_vector = checkDivKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); + } + // With broadcast + { + const bool is_with_broadcast = true; + test_kernel::TestDataFloatDiv test_data_kernel(is_with_broadcast); + std::vector output_data_vector = checkDivKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } } -TEST_F(DivTest, SInt64) -{ - checkInteger(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(DivTest, SInt32) -{ - checkInteger(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(DivTest, Input_Output_Type_NEG) +TEST_F(DivTest, Wrong_Input1_Type_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInput1WrongTypeDiv test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(DivTest, Invalid_Input_Type_NEG) +TEST_F(DivTest, Wrong_Input2_Type_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U64); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataInput2WrongTypeDiv test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(DivTest, Invalid_Output_Type_NEG) -{ - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S64); - - DivParams params{}; - params.activation = Activation::RELU; - - Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add tests for inplace optimizations for all types } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Elu.cpp b/onert-micro/luci-interpreter/src/kernels/Elu.cpp index 160a1a5..3c34591 100644 --- a/onert-micro/luci-interpreter/src/kernels/Elu.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Elu.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,38 +14,63 @@ * limitations under the License. */ -#include "kernels/Elu.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" #include "PALElu.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleElu(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + kernels::SISOKernel kernel(cur_op, runtime_graph); -Elu::Elu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} - -void Elu::configure() -{ - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input()) == + Tensor::num_elements(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) == Tensor::num_dims(kernel.output())); } -void Elu::execute() const +void execute_kernel_CircleElu(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (input()->element_type()) + kernels::SISOKernel kernel(cur_op, runtime_graph); + + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); + + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - luci_interpreter_pal::Elu(getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::Elu(flat_size, input_data_float, output_data_float); break; + } +#endif // DIS_FLOAT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -} -} // namespace kernels + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); +} } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Elu.test.cpp b/onert-micro/luci-interpreter/src/kernels/Elu.test.cpp index b87d687..71a9692 100644 --- a/onert-micro/luci-interpreter/src/kernels/Elu.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Elu.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,68 +14,73 @@ * limitations under the License. */ -#include "kernels/Elu.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/elu/FloatEluKernel.h" +#include "luci_interpreter/test_models/elu/NegEluKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -void Check(std::initializer_list input_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list output_data) +class EluTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + // Do nothing +}; + +template std::vector checkEluKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Elu kernel(&input_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - (void)output_shape; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST(EluTest, SimpleElu) +TEST_F(EluTest, Float_P) { - Check( - /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, - /*input_data=*/ - { - 0, -6, 2, -4, // - 3, -2, 10, -0.1, // - }, - /*output_data=*/ - { - 0.0, -0.997521, 2.0, -0.981684, // - 3.0, -0.864665, 10.0, -0.0951626, // - }); + test_kernel::TestDataFloatElu test_data_kernel; + std::vector output_data_vector = checkEluKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST(EluTest, InOutTypeMismatch_NEG) +TEST_F(EluTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, -6, 2, -4, // - 3, -2, 10, -0.1, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - Elu kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchEluKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Equal.cpp b/onert-micro/luci-interpreter/src/kernels/Equal.cpp index 003d643..76968a3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Equal.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Equal.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,128 +14,84 @@ * limitations under the License. */ -#include "kernels/Equal.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { - -Equal::Equal(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} - -void Equal::configure() +// TODO: reduce code duplication with less +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); -} + assert(x_data != nullptr); -void Equal::execute() const -{ - switch (x()->element_type()) - { - case DataType::FLOAT32: - evalFloat(); - break; - case DataType::S64: - evalInteger(); - break; - case DataType::S32: - evalInteger(); - break; - case DataType::U8: - evalQuantized(); - break; - default: - assert(false && "Unsupported type."); - } -} + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); -void Equal::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); if (op_params.is_broadcast) { - tflite::reference_ops::Broadcast4DSlowEqual(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + luci_interpreter_pal::BroadcastComparison4DSlowNoScaling( + op_params, kernels::getTensorShape(x), x_data, kernels::getTensorShape(y), y_data, + kernels::getTensorShape(output), output_data, luci_interpreter_pal::EqualFn); } else { - tflite::reference_ops::Equal(op_params, getTensorShape(x()), x_data, getTensorShape(y()), - y_data, getTensorShape(output()), output_data); + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::EqualFn); } } -template void Equal::evalInteger() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); +} // namespace - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); +void configure_kernel_CircleEqual(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::EqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); } -void Equal::evalQuantized() const +void execute_kernel_CircleEqual(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.left_shift = 8; - op_params.input1_offset = -x()->zero_point(); // Note the '-' - op_params.input1_shift = _x_shift; - op_params.input1_multiplier = _x_multiplier; - op_params.input2_offset = -y()->zero_point(); // Note the '-' - op_params.input2_shift = _y_shift; - op_params.input2_multiplier = _y_multiplier; - op_params.is_broadcast = x()->shape() != y()->shape(); + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowEqualWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else + switch (Tensor::element_type(kernel.input1())) { - tflite::reference_ops::EqualWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); + case DataType::S64: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; + case DataType::S32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Equal.h b/onert-micro/luci-interpreter/src/kernels/Equal.h deleted file mode 100644 index 7c5f380..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Equal.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_EQUAL_H -#define LUCI_INTERPRETER_KERNELS_EQUAL_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Equal : public Kernel -{ -public: - Equal(const Tensor *x, const Tensor *y, Tensor *output); - - const Tensor *x() const { return _inputs[0]; } - const Tensor *y() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; - -private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_EQUAL_H diff --git a/onert-micro/luci-interpreter/src/kernels/Equal.test.cpp b/onert-micro/luci-interpreter/src/kernels/Equal.test.cpp index 5870e54..15bcd19 100644 --- a/onert-micro/luci-interpreter/src/kernels/Equal.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Equal.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/Equal.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/equal/FloatEqualKernel.h" +#include "luci_interpreter/test_models/equal/IntEqualKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,277 +30,106 @@ using namespace testing; class EqualTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(EqualTest, FloatSimple) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, true, false, // Row 1 - false, true, false, // Row 2 - }; - - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(EqualTest, FloatBroardcast) +template +std::vector checkEqualKernel(test_kernel::TestDataBase *test_data_base) { - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - 0.9, 0.7, 0.5, // Row 4 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; - - std::vector ref_output_data{ - false, true, false, // Row 1 - false, false, false, // Row 2 - false, false, false, // Row 3 - true, true, true, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} - -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value, -2, max_value}; - - std::vector ref_output_data{true, false, true}; - - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); -} + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -2, -3, // Row 3 - min_value, -2, max_value, // Row 4 - }; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - std::vector y_data{ - min_value, -2, max_value, // Row 1 - }; + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - std::vector ref_output_data{ - true, false, false, // Row 1 - false, false, true, // Row 2 - false, true, false, // Row 3 - true, true, true, // Row 4 - }; + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + runtime_module.execute(); - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(EqualTest, Int32) +TEST_F(EqualTest, FloatNoBroadcast_P) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(EqualTest, Int64) +TEST_F(EqualTest, FloatWithBroadcast_P) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = true; + test_kernel::TestDataFloatEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(EqualTest, Uint8Quantized) +TEST_F(EqualTest, FloatNoBroadcast_NEG) { - std::vector x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.5, 0.55, 0.5, // Row 1 - -1, 0, 0.05, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, true, false, false, // Row 1 - false, true, true, false, // Row 2 - }; - - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - - std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 2); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkEqualKernel(&test_data_kernel), ""); } -TEST_F(EqualTest, Uint8QuantizedBroadcast) +TEST_F(EqualTest, FloatWithBroadcast_NEG) { - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - -1, 0.05, 0, 1, // Row 4 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - false, false, false, false, // Row 1 - false, false, true, false, // Row 2 - false, false, false, false, // Row 3 - true, true, true, true, // Row 4 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 4, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + const bool is_with_broadcast = true; + test_kernel::TestDataFloatEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkEqualKernel(&test_data_kernel), ""); } -TEST_F(EqualTest, Input_Type_Mismatch_NEG) +TEST_F(EqualTest, IntWithBroadcast_P) { - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = true; + test_kernel::TestDataIntEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(EqualTest, Input_Output_Type_NEG) +TEST_F(EqualTest, IntNoBroadcast_P) { - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataIntEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(EqualTest, Float_Broadcast_NEG) +TEST_F(EqualTest, IntWithBroadcast_NEG) { - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = true; + test_kernel::TestDataIntEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkEqualKernel(&test_data_kernel), ""); } -TEST_F(EqualTest, Int32_Broadcast_NEG) +TEST_F(EqualTest, IntNoBroadcast_NEG) { - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); -} - -TEST_F(EqualTest, Int64_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Equal kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataIntEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkEqualKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Exp.cpp b/onert-micro/luci-interpreter/src/kernels/Exp.cpp index ceb54e7..b46b354 100644 --- a/onert-micro/luci-interpreter/src/kernels/Exp.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Exp.cpp @@ -15,43 +15,64 @@ * limitations under the License. */ -#include "kernels/Exp.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include +#include "PALExp.h" namespace luci_interpreter { -namespace kernels -{ -Exp::Exp(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} - -void Exp::configure() +void configure_kernel_CircleExp(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); + kernels::SISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input()) == + Tensor::num_elements(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) == Tensor::num_dims(kernel.output())); } -void Exp::execute() const +void execute_kernel_CircleExp(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (input()->element_type()) + kernels::SISOKernel kernel(cur_op, runtime_graph); + + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); + + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::Exp(flat_size, input_data_float, output_data_float); break; + } +#endif // DIS_FLOAT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -} -void Exp::evalFloat() const -{ - const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); - tflite::reference_ops::Exp(getTensorData(input()), size, getTensorData(output())); + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Exp.test.cpp b/onert-micro/luci-interpreter/src/kernels/Exp.test.cpp index a159d9d..21d21e7 100644 --- a/onert-micro/luci-interpreter/src/kernels/Exp.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Exp.test.cpp @@ -15,41 +15,73 @@ * limitations under the License. */ -#include "kernels/Exp.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/exp/FloatExpKernel.h" +#include "luci_interpreter/test_models/exp/NegExpKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -TEST(ExpTest, Float) +class ExpTest : public ::testing::Test +{ + // Do nothing +}; + +template std::vector checkExpKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(ExpTest, Float_P) +{ + test_kernel::TestDataFloatExp test_data_kernel; + std::vector output_data_vector = checkExpKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); +} + +TEST_F(ExpTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - Shape input_shape{1, 1, 7}; - std::vector input_data{0.0f, 1.0f, -1.0f, 100.0f, -100.0f, 0.01f, -0.01f}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Exp kernel(&input_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_shape{1, 1, 7}; - std::vector ref_output_data{std::exp(0.0f), std::exp(1.0f), std::exp(-1.0f), - std::exp(100.0f), std::exp(-100.0f), std::exp(0.01f), - std::exp(-0.01f)}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::NegTestDataInputOutputTypeMismatchExpKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ExpandDims.cpp b/onert-micro/luci-interpreter/src/kernels/ExpandDims.cpp index 867af01..1035bb8 100644 --- a/onert-micro/luci-interpreter/src/kernels/ExpandDims.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ExpandDims.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +40,7 @@ void configure_kernel_CircleExpandDims(const circle::Operator *cur_op, assert(output != nullptr); auto axis_data = runtime_graph->getConstDataByTensor(axis); + assert(axis_data != nullptr); int32_t axis_value; @@ -65,7 +65,7 @@ void configure_kernel_CircleExpandDims(const circle::Operator *cur_op, } void execute_kernel_CircleExpandDims(const circle::Operator *cur_op, - BaseRuntimeGraph *runtime_graph, bool is_inplace) + BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); const auto output_index = cur_op->outputs()->operator[](0); @@ -76,6 +76,8 @@ void execute_kernel_CircleExpandDims(const circle::Operator *cur_op, const auto input = runtime_graph->getCircleTensorByIndex(input_index); const auto output = runtime_graph->getCircleTensorByIndex(output_index); + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + if (is_inplace) { runtime_graph->makeInplaceOperation(input, output); diff --git a/onert-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp b/onert-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp index 1ebbcd8..b7448cd 100644 --- a/onert-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ExpandDims.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2017 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +14,13 @@ * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/ExpandDims.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/expand_dims/ExpandDimsKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -32,87 +28,59 @@ using namespace testing; class ExpandDimsTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(ExpandDimsTest, PositiveAxis) +template +std::vector checkExpandDimsKernel(test_kernel::TestDataBase *test_data_base) { - std::vector input_data{-1, 1, -2, 2}; - std::initializer_list input_shape = {2, 2}; - - std::initializer_list axis_value = {0}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(input_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2})); -} + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); -TEST_F(ExpandDimsTest, NegAxis) -{ - std::vector input_data{-1, 1, -2, 2}; - std::initializer_list input_shape = {2, 2}; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - std::initializer_list axis_value = {-1}; + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); + runtime_module.execute(); - ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(input_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 2, 1})); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(ExpandDimsTest, InvalidAxisType_NEG) +TEST_F(ExpandDimsTest, MainTest_P) { - std::vector input_data{-1, 1, -2, 2}; - std::initializer_list input_shape = {2, 2}; - - std::initializer_list axis_value = {1.0}; - - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataExpandDimsKernel test_data_kernel; + std::vector output_data_vector = checkExpandDimsKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(ExpandDimsTest, InvalidAxisValue_NEG) +TEST_F(ExpandDimsTest, WrongAxisType_NEG) { - std::vector input_data{-1, 1, -2, 2}; - std::initializer_list input_shape = {2, 2}; - - std::initializer_list axis_value = {3}; - - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor axis_tensor = makeInputTensor({1}, axis_value, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - ExpandDims kernel(&input_tensor, &axis_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInvalidInputTypeExpandDimsKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/Fill.cpp b/onert-micro/luci-interpreter/src/kernels/Fill.cpp index 6707c70..8bc5014 100644 --- a/onert-micro/luci-interpreter/src/kernels/Fill.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Fill.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. 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. @@ -15,103 +15,70 @@ * limitations under the License. */ -#include "kernels/Fill.h" +#include "Builders.h" +#include "TISOKernel.h" #include "kernels/Utils.h" -#include "PALFill.h" namespace luci_interpreter { -namespace kernels +namespace { -Fill::Fill(const Tensor *dims, const Tensor *value, Tensor *output) - : Kernel({dims, value}, {output}) +template void fillImpl(const size_t flat_size, const T *value_data, T *output_data) { -} - -template void Fill::configureShape() -{ - const auto dims_data = getTensorData(dims()); - Shape output_shape(dims()->shape().dim(0)); - - for (int i = 0; i < output_shape.num_dims(); ++i) + for (int i = 0; i < flat_size; ++i) { - T data = dims_data[i]; - if (data < 0) - assert(false && "Fill dimensions must be >= 0"); - - output_shape.dim(i) = data; + output_data[i] = *value_data; } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); } -void Fill::configure() +} // namespace + +void configure_kernel_CircleFill(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto dims_shape = dims()->shape(); - const auto value_shape = value()->shape(); + kernels::TISOKernel kernel(cur_op, runtime_graph); + // value tensor must be a scalar or has one element + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input2()) == 0 or + Tensor::num_elements(kernel.input2()) == 1); + // value and output type must match + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == + Tensor::element_type(kernel.output())); +} - // Make sure the 1st input tensor is 1-D - LUCI_INTERPRETER_CHECK(dims_shape.num_dims() == 1); +void execute_kernel_CircleFill(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); - // Make sure the 1st input tensor is int32 or int64 - LUCI_INTERPRETER_CHECK(dims()->element_type() == DataType::S32 or - dims()->element_type() == DataType::S64); + const circle::Tensor *value = kernel.input2(); + const circle::Tensor *output = kernel.output(); - // Make sure the 2nd input tensor is a scalar - LUCI_INTERPRETER_CHECK(value_shape.num_dims() == 0) + kernels::TISOData tiso_data = kernel.readData(); + const uint8_t *value_data = tiso_data.input2_data; + uint8_t *output_data = tiso_data.output_data; - // Check zero point and scale for S16 and S8 - if (value()->element_type() == DataType::S16 or value()->element_type() == DataType::S8) - { - LUCI_INTERPRETER_CHECK(value()->scale() == output()->scale()); - LUCI_INTERPRETER_CHECK(value()->zero_point() == output()->zero_point()); + const size_t flat_size = Tensor::num_elements(output); - if (value()->element_type() == DataType::S16) - LUCI_INTERPRETER_CHECK(value()->zero_point() == 0); - } - // Resize output - switch (dims()->element_type()) + switch (Tensor::element_type(value)) { - case DataType::S32: - configureShape(); - break; - case DataType::S64: - configureShape(); - break; - default: - assert(false && "Unsupported type."); - } -} - -void Fill::execute() const -{ - switch (output()->element_type()) - { - case DataType::S8: - tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), - getTensorShape(output()), getTensorData(output())); - break; - case DataType::S16: - tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), - getTensorShape(output()), getTensorData(output())); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + fillImpl(flat_size, kernels::getTensorData(value_data), + kernels::getTensorData(output_data)); break; +#endif // DIS_FLOAT case DataType::S32: - tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), - getTensorShape(output()), getTensorData(output())); + fillImpl(flat_size, kernels::getTensorData(value_data), + kernels::getTensorData(output_data)); break; - case DataType::S64: - tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), - getTensorShape(output()), getTensorData(output())); - break; - case DataType::FLOAT32: - tflite::reference_ops::Fill(getTensorShape(value()), getTensorData(value()), - getTensorShape(output()), getTensorData(output())); +#ifndef DIS_QUANT + case DataType::U8: + fillImpl(flat_size, kernels::getTensorData(value_data), + kernels::getTensorData(output_data)); break; +#endif // DIS_QUANT default: - assert(false && "Unsupported type."); + assert(false && "Not impl yet"); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Fill.h b/onert-micro/luci-interpreter/src/kernels/Fill.h deleted file mode 100644 index 4e517c5..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Fill.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_FILL_H -#define LUCI_INTERPRETER_KERNELS_FILL_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Fill : public Kernel -{ -public: - Fill(const Tensor *dims, const Tensor *value, Tensor *output); - - const Tensor *dims() const { return _inputs[0]; } - const Tensor *value() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - template void configureShape(); -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_FILL_H diff --git a/onert-micro/luci-interpreter/src/kernels/Fill.test.cpp b/onert-micro/luci-interpreter/src/kernels/Fill.test.cpp index 4033caa..a95c7a3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Fill.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Fill.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +14,14 @@ * limitations under the License. */ -#include "kernels/Fill.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/fill/FillKernel.h" +#include "luci_interpreter/test_models/fill/NegFillKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,141 +29,69 @@ using namespace testing; class FillTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -template void runFillIntKernel(IMemoryManager *memory_manager) +template std::vector checkFillKernel(test_kernel::TestDataBase *test_data_base) { - Shape dims_shape{2}; - - std::vector dims_data = {2, 3}; - std::vector value_data = {5}; - - Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); - Tensor value = makeInputTensor
(/*scalar*/ {}, value_data, memory_manager); - - Tensor output_tensor = makeOutputTensor(DT); - - Fill kernel(&dims, &value, &output_tensor); - - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{5, 5, 5, 5, 5, 5}; - EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); - - std::vector ref_output_shape{2, 3}; - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} - -template void runFillQuantIntKernel(IMemoryManager *memory_manager) -{ - Shape dims_shape{2}; - - std::vector dims_data = {2, 3}; - std::vector value_data = {5}; - - int32_t zero_point = 0; - - if (DT == loco::DataType::S8) - zero_point = 1; - - Tensor dims = makeInputTensor(dims_shape, dims_data, memory_manager); - Tensor value = makeInputTensor
(/*scalar*/ {}, /*scale*/ 0.25, /*zero_point*/ zero_point, - value_data, memory_manager); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Tensor output_tensor = makeOutputTensor(DT, /*scale*/ 0.25, /*zero_point*/ zero_point); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Fill kernel(&dims, &value, &output_tensor); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - std::vector ref_output_data{5, 5, 5, 5, 5, 5}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + runtime_module.execute(); - std::vector ref_output_shape{2, 3}; - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); -} + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -TEST_F(FillTest, FillInt) -{ - // Run for int32_t input - runFillIntKernel(_memory_manager.get()); - // Run for int64_t input - runFillIntKernel(_memory_manager.get()); - // Run for int8_t input - runFillQuantIntKernel(_memory_manager.get()); - // Run for int16_t input - runFillQuantIntKernel(_memory_manager.get()); - - SUCCEED(); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(FillTest, FillFloat) +TEST_F(FillTest, MainTest_P) { - Shape dims_shape{3}; - - std::vector dims_data = {2, 2, 2}; - std::vector value_data = {5}; - - Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); - Tensor value = - makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); - - Fill kernel(&dims, &value, &output_tensor); - - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{5, 5, 5, 5, 5, 5, 5, 5}; - - std::vector ref_output_shape{2, 2, 2}; - EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::TestDataFillKernel test_data_kernel; + std::vector output_data_vector = checkFillKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(FillTest, Invalid_Input_Shape_NEG) +TEST_F(FillTest, Input_output_type_mismatch_NEG) { - Shape dims_shape{1, 3}; - - std::vector dims_data = {2, 2, 2}; - std::vector value_data = {5}; - - Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); - Tensor value = - makeInputTensor(/*scalar*/ {}, value_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); - - Fill kernel(&dims, &value, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputTypeMismatchFillKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(FillTest, Invalid_Value_Shape_NEG) +TEST_F(FillTest, Wrong_input_shape_NEG) { - Shape dims_shape{3}; - - std::vector dims_data = {2, 2, 2}; - std::vector value_data = {5}; - - Tensor dims = makeInputTensor(dims_shape, dims_data, _memory_manager.get()); - Tensor value = makeInputTensor({1}, value_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); - - Fill kernel(&dims, &value, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataWrongInputShapeFillKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Floor.h b/onert-micro/luci-interpreter/src/kernels/Floor.h index 47d1124..ca3ad59 100644 --- a/onert-micro/luci-interpreter/src/kernels/Floor.h +++ b/onert-micro/luci-interpreter/src/kernels/Floor.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Floor.test.cpp b/onert-micro/luci-interpreter/src/kernels/Floor.test.cpp index 37523be..30076fb 100644 --- a/onert-micro/luci-interpreter/src/kernels/Floor.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Floor.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/FloorDiv.h b/onert-micro/luci-interpreter/src/kernels/FloorDiv.h index 7a0d6b1..e9c47d8 100644 --- a/onert-micro/luci-interpreter/src/kernels/FloorDiv.h +++ b/onert-micro/luci-interpreter/src/kernels/FloorDiv.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/FullyConnected.cpp b/onert-micro/luci-interpreter/src/kernels/FullyConnected.cpp index 2cfdc84..0493eaa 100644 --- a/onert-micro/luci-interpreter/src/kernels/FullyConnected.cpp +++ b/onert-micro/luci-interpreter/src/kernels/FullyConnected.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -34,10 +33,9 @@ void evalFloat(const circle::Tensor *input, const circle::Tensor *weights, kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), &activation_min, &activation_max); - tflite::FullyConnectedParams params{}; + luci_interpreter_pal::FullyConnectedParams params{}; params.float_activation_min = activation_min; params.float_activation_max = activation_max; - params.weights_format = tflite::FullyConnectedWeightsFormat::kDefault; auto *input_data = runtime_graph->getDataByTensor(input); auto *output_data = runtime_graph->getDataByTensor(output); @@ -49,11 +47,19 @@ void evalFloat(const circle::Tensor *input, const circle::Tensor *weights, assert(weights_data != nullptr); assert(output_data != nullptr); - tflite::reference_ops::FullyConnected( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(weights), kernels::getTensorData(weights_data), - kernels::getTensorShape(bias), kernels::getTensorData(bias_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); + int32_t input_shape[kMaxSmallSize]; + kernels::getTensorDims(input, runtime_graph, input_shape); + + int32_t weight_shape[kMaxSmallSize]; + kernels::getTensorDims(weights, runtime_graph, weight_shape); + + int32_t output_shape[kMaxSmallSize]; + kernels::getTensorDims(output, runtime_graph, output_shape); + + luci_interpreter_pal::FullyConnected( + params, input_shape, kernels::getTensorData(input_data), weight_shape, + kernels::getTensorData(weights_data), kernels::getTensorData(bias_data), + output_shape, kernels::getTensorData(output_data)); } #ifndef DIS_QUANT @@ -77,7 +83,7 @@ void evalQuantized(const circle::Tensor *input, const circle::Tensor *weights, int32_t filter_offset = -Tensor::zero_point(weights); int32_t output_offset = Tensor::zero_point(output); - tflite::FullyConnectedParams op_params{}; + luci_interpreter_pal::FullyConnectedParams op_params{}; op_params.input_offset = input_offset; op_params.weights_offset = filter_offset; op_params.output_offset = output_offset; @@ -98,65 +104,24 @@ void evalQuantized(const circle::Tensor *input, const circle::Tensor *weights, assert(weights_data != nullptr); assert(output_data != nullptr); - tflite::reference_ops::FullyConnected( - op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(weights), kernels::getTensorData(weights_data), - kernels::getTensorShape(bias), kernels::getTensorData(bias_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); -} - -void evalQuantizedS8(const circle::Tensor *input, const circle::Tensor *weights, - const circle::Tensor *bias, const circle::Tensor *output, - const circle::FullyConnectedOptions *options, BaseRuntimeGraph *runtime_graph) -{ - double real_multiplier = 0.0; - int output_shift; - int32_t output_activation_min; - int32_t output_activation_max; - int32_t output_multiplier; - real_multiplier = kernels::getQuantizedConvolutionMultipler( - Tensor::scale(input), Tensor::scale(weights), Tensor::scale(output)); - kernels::quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); - kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), - output, &output_activation_min, - &output_activation_max); - - int32_t input_offset = -Tensor::zero_point(input); - int32_t filter_offset = -Tensor::zero_point(weights); - int32_t output_offset = Tensor::zero_point(output); - - 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; + int32_t input_shape[kMaxSmallSize]; + kernels::getTensorDims(input, runtime_graph, input_shape); - auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); + int32_t weights_shape[kMaxSmallSize]; + kernels::getTensorDims(weights, runtime_graph, weights_shape); - auto *weights_data = runtime_graph->getConstDataByTensor(weights); - auto *bias_data = runtime_graph->getConstDataByTensor(bias); - - assert(input_data != nullptr); - assert(weights_data != nullptr); - assert(output_data != nullptr); + int32_t output_shape[kMaxSmallSize]; + kernels::getTensorDims(output, runtime_graph, output_shape); - luci_interpreter_pal::FullyConnected( - op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(weights), kernels::getTensorData(weights_data), - kernels::getTensorShape(bias), kernels::getTensorData(bias_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); + luci_interpreter_pal::FullyConnected( + op_params, input_shape, kernels::getTensorData(input_data), weights_shape, + kernels::getTensorData(weights_data), kernels::getTensorData(bias_data), + output_shape, kernels::getTensorData(output_data)); } #endif } // namespace -// TODO think how remove unused param void configure_kernel_CircleFullyConnected(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { @@ -207,7 +172,11 @@ void configure_kernel_CircleFullyConnected(const circle::Operator *cur_op, LUCI_INTERPRETER_CHECK(Tensor::num_dims(weights) == 2); LUCI_INTERPRETER_CHECK(bias == nullptr || Tensor::num_elements(bias) == Tensor::dim(weights, 0)); - LUCI_INTERPRETER_CHECK(Tensor::num_elements(input) % Tensor::dim(weights, 1) == 0); + +#ifdef DIS_DYN_SHAPES + int32_t input_num_elements = Tensor::num_elements(input); + LUCI_INTERPRETER_CHECK(input_num_elements % Tensor::dim(weights, 1) == 0); +#endif // DIS_DYN_SHAPES if (bias) LUCI_INTERPRETER_CHECK(Tensor::num_elements(bias) == Tensor::dim(weights, 0)); @@ -220,7 +189,7 @@ void configure_kernel_CircleFullyConnected(const circle::Operator *cur_op, // TODO think how remove unused param void execute_kernel_CircleFullyConnected(const circle::Operator *cur_op, - BaseRuntimeGraph *runtime_graph, bool) + BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); const auto weight_index = cur_op->inputs()->operator[](1); @@ -248,9 +217,6 @@ void execute_kernel_CircleFullyConnected(const circle::Operator *cur_op, case DataType::U8: evalQuantized(input, weights, bias, output, options, runtime_graph); break; - case DataType::S8: - evalQuantizedS8(input, weights, bias, output, options, runtime_graph); - break; #endif // DIS_QUANT #ifndef DIS_FLOAT case DataType::FLOAT32: diff --git a/onert-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp b/onert-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp index feda0d8..cbccd5d 100644 --- a/onert-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/FullyConnected.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -14,250 +13,112 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/FullyConnected.h" + #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/fully_connected/FloatFullyConnectedKernel.h" +#include "luci_interpreter/test_models/fully_connected/U8FullyConnectedKernel.h" +#include "luci_interpreter/test_models/fully_connected/NegFullyConnectedKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template -void Check(std::initializer_list input_shape, std::initializer_list weights_shape, - std::initializer_list bias_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list weights_data, - std::initializer_list bias_data, std::initializer_list output_data) +class FullyConnectedTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, weights_data, memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - FullyConnectedParams params{}; - params.activation = Activation::RELU; - - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); -} + // Do nothing +}; -template <> -void Check(std::initializer_list input_shape, - std::initializer_list weights_shape, - std::initializer_list bias_shape, - std::initializer_list output_shape, - std::initializer_list input_data, - std::initializer_list weights_data, - std::initializer_list bias_data, std::initializer_list output_data) +template +std::vector checkFullyConnectedKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - const float quantized_tolerance = getTolerance(-127, 128, 255); - std::pair input_quant_param = quantizationParams(-63.5, 64); - std::pair output_quant_param = quantizationParams(-127, 128); - Tensor input_tensor = - makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, - input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, input_quant_param.first, input_quant_param.second, - weights_data, memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, input_quant_param.first * input_quant_param.first, 0, - bias_data, memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(DataType::S8, output_quant_param.first, output_quant_param.second); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - FullyConnectedParams params{}; - params.activation = Activation::RELU; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data, quantized_tolerance)); -} + // set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -template <> -void Check( - std::initializer_list input_shape, std::initializer_list weights_shape, - std::initializer_list bias_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list weights_data, - std::initializer_list bias_data, std::initializer_list output_data) -{ - std::unique_ptr memory_manager = std::make_unique(); - const float quantized_tolerance = getTolerance(-127, 128, 255); - std::pair input_quant_param = quantizationParams(-63.5, 64); - std::pair output_quant_param = quantizationParams(-127, 128); - Tensor input_tensor = - makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, - input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, input_quant_param.first, input_quant_param.second, - weights_data, memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, input_quant_param.first * input_quant_param.first, 0, - bias_data, memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + runtime_module.execute(); - FullyConnectedParams params{}; - params.activation = Activation::RELU; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data, quantized_tolerance)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template class FullyConnectedTest : public ::testing::Test +TEST_F(FullyConnectedTest, Float_P) { -}; - -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(FullyConnectedTest, DataTypes); + test_kernel::TestDataFloatFullyConnected test_data_kernel; + std::vector output_data_vector = checkFullyConnectedKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); +} -TYPED_TEST(FullyConnectedTest, Simple) +TEST_F(FullyConnectedTest, U8_P) { - Check({3, 2, 2, 1}, {3, 6}, {3}, {2, 3}, - { - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 - }, - { - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 - }, - {-1, -5, -8}, - { - 0, 0, 32, // batch = 0 - 22, 11, 47, // batch = 1 - }); + test_kernel::TestDataU8FullyConnected test_data_kernel; + std::vector output_data_vector = checkFullyConnectedKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(FullyConnectedTest, InvalidBiasType_NEG) +TEST_F(FullyConnectedTest, Wrong_weight_type_NEG) { - Shape input_shape{3, 2, 2, 1}; - std::vector input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 - }; - Shape weights_shape{3, 6}; - std::vector weights_data{ - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 - }; - Shape bias_shape{3}; - std::vector bias_data{-1, -5, -8}; - - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, weights_data, memory_manager.get()); - Tensor bias_tensor = makeInputTensor(bias_shape, bias_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - FullyConnectedParams params{}; - params.activation = Activation::RELU; - - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataWrongWeightTypeFullyConnectedKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(FullyConnectedTest, InvalidWeightShapeDim_NEG) +TEST_F(FullyConnectedTest, Wrong_weight_shape_NEG) { - Shape input_shape{3, 2, 2, 1}; - std::vector input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 - }; - Shape weights_shape{1, 3, 6}; - std::vector weights_data{ - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 - }; - Shape bias_shape{3}; - std::vector bias_data{-1, -5, -8}; - - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, weights_data, memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - FullyConnectedParams params{}; - params.activation = Activation::RELU; - - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataWrongWeightShapeFullyConnectedKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(FullyConnectedTest, BiasElementNumWeightDimMismatch_NEG) +TEST_F(FullyConnectedTest, Wrong_bias_shape_NEG) { - Shape input_shape{3, 2, 2, 1}; - std::vector input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 - }; - Shape weights_shape{6, 3}; - std::vector weights_data{ - -3, -7, 4, // unit = 0 - -4, -6, 4, // unit = 1 - 3, 5, 2, // unit = 2 - 3, -3, -8, // unit = 3 - -3, 7, 4, // unit = 4 - 9, 0, -5, // unit = 5 - }; - Shape bias_shape{3}; - std::vector bias_data{-1, -5, -8}; - - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor weights_tensor = - makeInputTensor(weights_shape, weights_data, memory_manager.get()); - Tensor bias_tensor = - makeInputTensor(bias_shape, bias_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - FullyConnectedParams params{}; - params.activation = Activation::RELU; - - FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataWrongBiasShapeFullyConnectedKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } +// TODO: add tests for S8 + } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/Gather.cpp b/onert-micro/luci-interpreter/src/kernels/Gather.cpp index d26b718..b146b89 100644 --- a/onert-micro/luci-interpreter/src/kernels/Gather.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Gather.cpp @@ -15,125 +15,146 @@ * limitations under the License. */ -#include "kernels/Gather.h" +#include "Builders.h" #include "kernels/Utils.h" -#include "PALGather.h" +#include "TISOKernel.h" #include namespace luci_interpreter { - -namespace kernels +namespace { -Gather::Gather(const Tensor *params, const Tensor *indices, Tensor *output, - const GatherParams &gparams) - : KernelWithParams({params, indices}, {output}, gparams) +template +void gather(const circle::GatherOptions *options, kernels::TISOKernel *kernel) { -} - -void Gather::configure() -{ - if (params()->element_type() == DataType::FLOAT32) - { - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32); - } - else - { - assert(false && "Unsupported type."); - } - - LUCI_INTERPRETER_CHECK(indices()->element_type() == DataType::S32 || - indices()->element_type() == DataType::S64); + kernels::TISOData tiso_data = kernel->readData(); - // refer tensorflow/lite/kernels/gather.cc + const InputT *input_data = kernels::getTensorData(tiso_data.input1_data); + const CoordsT *coords_data = kernels::getTensorData(tiso_data.input2_data); + InputT *output_data = kernels::getTensorData(tiso_data.output_data); - const Shape ¶ms_shape = params()->shape(); - const Shape &indices_shape = indices()->shape(); + const circle::Tensor *input = kernel->input1(); + const circle::Tensor *coords = kernel->input2(); - int axis = _params.axis; + const int input_dims_size = Tensor::num_dims(input); + int axis = options->axis(); if (axis < 0) { - axis += params_shape.num_dims(); + axis += input_dims_size; } - LUCI_INTERPRETER_CHECK(0 <= axis && axis < params_shape.num_dims()); - int batch_dims = _params.batch_dims; - // batch_dims should be in range: [-rank(indices), rank(indices)]. - // Negative batch_dims is added with rank of positions. + int batch_dims = options->batch_dims(); + // batch_dims should be in range: [-rank(coords), rank(coords)]. + // Negative batch_dims is added with rank of coords. + const int coords_dims_size = Tensor::num_dims(coords); if (batch_dims < 0) { - batch_dims += indices_shape.num_dims(); + batch_dims += coords_dims_size; } - LUCI_INTERPRETER_CHECK(batch_dims <= axis); - LUCI_INTERPRETER_CHECK(0 <= batch_dims && batch_dims < params_shape.num_dims()); - LUCI_INTERPRETER_CHECK(batch_dims <= indices_shape.num_dims()); + + const int axis_size = Tensor::dim(input, axis); + + int batch_size = 1; for (int i = 0; i < batch_dims; ++i) { - LUCI_INTERPRETER_CHECK(params_shape.dim(i) == indices_shape.dim(i)); + batch_size *= Tensor::dim(input, i); } - - const int num_dimensions = params_shape.num_dims() + indices_shape.num_dims() - 1 - batch_dims; - - Shape output_shape(num_dimensions); - int output_index = 0; - for (int i = 0; i < axis; ++i) + int outer_size = 1; + for (int i = batch_dims; i < axis; ++i) { - output_shape.dim(output_index++) = params_shape.dim(i); + outer_size *= Tensor::dim(input, i); } - for (int i = batch_dims; i < indices_shape.num_dims(); ++i) + int inner_size = 1; + for (int i = axis + 1; i < input_dims_size; ++i) { - output_shape.dim(output_index++) = indices_shape.dim(i); + inner_size *= Tensor::dim(input, i); } - for (int i = axis + 1; i < params_shape.num_dims(); ++i) + int coord_size = 1; + for (int i = batch_dims; i < coords_dims_size; ++i) { - output_shape.dim(output_index++) = params_shape.dim(i); + coord_size *= Tensor::dim(coords, i); } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); -} -void Gather::execute() const -{ - switch (params()->element_type()) + for (int batch = 0; batch < batch_size; ++batch) { - case DataType::FLOAT32: - evalFloat(); - break; - default: - assert(false && "Unsupported type."); + for (int outer = 0; outer < outer_size; ++outer) + { + for (int coord = 0; coord < coord_size; ++coord) + { + auto x = coords_data[coord]; + std::memcpy( + output_data + (((batch * outer_size) + outer) * coord_size + coord) * inner_size, + input_data + + (((batch * outer_size) + outer) * axis_size + coords_data[batch * coord_size + coord]) * + inner_size, + sizeof(InputT) * inner_size); + } + } } } -void Gather::evalFloat() const +} // namespace + +void configure_kernel_CircleGather(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - assert(indices()->element_type() == DataType::S32 || indices()->element_type() == DataType::S64); + kernels::TISOKernel kernel(cur_op, runtime_graph); - const auto params_data = getTensorData(params()); - auto output_data = getTensorData(output()); + const auto *options = cur_op->builtin_options_as_GatherOptions(); - tflite::GatherParams tparams; - tparams.axis = _params.axis; - tparams.batch_dims = _params.batch_dims; + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == DataType::FLOAT32 or + Tensor::element_type(kernel.input1()) == DataType::S8 or + Tensor::element_type(kernel.input1()) == DataType::S32); - if (indices()->element_type() == DataType::S32) + int32_t axis = options->axis(); + int32_t num_dims = Tensor::num_dims(kernel.input1()); + if (axis < 0) { - const auto indices_data = getTensorData(indices()); + axis += num_dims; + } + + LUCI_INTERPRETER_CHECK(axis >= 0 and axis < num_dims); - luci_interpreter_pal::Gather(tparams, getTensorShape(params()), params_data, - getTensorShape(indices()), indices_data, - getTensorShape(output()), output_data); + int32_t batch_dims = options->batch_dims(); + int32_t coords_num_dims = Tensor::num_dims(kernel.input2()); + // batch_dims should be in range: [-rank(coords), rank(coords)]. + // Negative batch_dims is added with rank of coords. + if (batch_dims < 0) + { + batch_dims += coords_num_dims; } - else + LUCI_INTERPRETER_CHECK(batch_dims <= axis); + LUCI_INTERPRETER_CHECK(batch_dims >= 0 and batch_dims < num_dims); + LUCI_INTERPRETER_CHECK(batch_dims <= coords_num_dims); + for (int i = 0; i < batch_dims; ++i) { - const auto indices_data = getTensorData(indices()); + LUCI_INTERPRETER_CHECK(Tensor::dim(kernel.input1(), i) == Tensor::dim(kernel.input2(), i)); + } +} + +void execute_kernel_CircleGather(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); - luci_interpreter_pal::Gather(tparams, getTensorShape(params()), params_data, - getTensorShape(indices()), indices_data, - getTensorShape(output()), output_data); + const auto *options = cur_op->builtin_options_as_GatherOptions(); + + switch (Tensor::element_type(kernel.input1())) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + return gather(options, &kernel); +#endif // DIS_FLOAT +#ifndef DIS_QUANT + case DataType::S8: + return gather(options, &kernel); +#endif // DIS_QUANT + case DataType::S32: + return gather(options, &kernel); + default: + assert(false && "Unsupported type"); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Gather.h b/onert-micro/luci-interpreter/src/kernels/Gather.h deleted file mode 100644 index 5a36c82..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Gather.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_GATHER_H -#define LUCI_INTERPRETER_KERNELS_GATHER_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Gather : public KernelWithParams -{ -public: - Gather(const Tensor *params, const Tensor *indices, Tensor *output, const GatherParams &gparams); - - const Tensor *params() const { return _inputs[0]; } - const Tensor *indices() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_GATHER_H diff --git a/onert-micro/luci-interpreter/src/kernels/Gather.test.cpp b/onert-micro/luci-interpreter/src/kernels/Gather.test.cpp index dacf366..c1fa5ef 100644 --- a/onert-micro/luci-interpreter/src/kernels/Gather.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Gather.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +14,15 @@ * limitations under the License. */ -#include "kernels/Gather.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/gather/FloatGatherKernel.h" +#include "luci_interpreter/test_models/gather/IntGatherKernel.h" +#include "luci_interpreter/test_models/gather/NegGatherKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,109 +30,93 @@ using namespace testing; class GatherTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(GatherTest, Simple) +template std::vector checkGatherKernel(test_kernel::TestDataBase *test_data_base) { - std::vector params_data{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - std::vector indices_data{1, 0, 1, 5}; - std::vector ref_output_data{2.f, 1.f, 2.f, 6.f}; - - Tensor params_tensor = - makeInputTensor({1, 6}, params_data, _memory_manager.get()); - Tensor indices_tensor = makeInputTensor({4}, indices_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - GatherParams gparams; - - gparams.axis = 1; - gparams.batch_dims = 0; - - Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4})); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(GatherTest, Simple_Batch) +TEST_F(GatherTest, Gather_Float_P) { - Shape params_shape = {3, 5}; - Shape indices_shape = {3, 2}; - std::vector params_data{0., 0., 1., 0., 2., 3., 0., 0., 0., 4., 0., 5., 0., 6., 0.}; - std::vector indices_data{2, 4, 0, 4, 1, 3}; - std::vector ref_output_data{1., 2., 3., 4., 5., 6.}; - - Tensor params_tensor = - makeInputTensor(params_shape, params_data, _memory_manager.get()); - Tensor indices_tensor = - makeInputTensor(indices_shape, indices_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - GatherParams gparams; - - gparams.axis = 1; - gparams.batch_dims = 1; - - Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 2})); + test_kernel::TestDataFloatGather test_data_float_gather; + std::vector output_data_vector = checkGatherKernel(&test_data_float_gather); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_float_gather.get_output_data_by_index(0), 0.0001f)); } -TEST_F(GatherTest, Simple_NEG) +TEST_F(GatherTest, Gather_Int_P) { - Tensor params_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor indices_tensor = makeInputTensor({1}, {0}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - GatherParams gparams; - - Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataIntGather test_data_int_gather; + std::vector output_data_vector = checkGatherKernel(&test_data_int_gather); + EXPECT_THAT(output_data_vector, test_data_int_gather.get_output_data_by_index(0)); } -TEST_F(GatherTest, Axis_NEG) +TEST_F(GatherTest, Input_output_type_mismatch_NEG) { - Tensor params_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor indices_tensor = makeInputTensor({1}, {0}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - GatherParams gparams; - - gparams.axis = 100; - gparams.batch_dims = 0; - - Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchGatherKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(GatherTest, Batch_NEG) +TEST_F(GatherTest, Wrong_position_type_NEG) { - std::vector params_data{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - std::vector indices_data{1, 0, 1, 5}; - std::vector ref_output_data{2.f, 1.f, 2.f, 6.f}; - - Tensor params_tensor = - makeInputTensor({1, 6}, params_data, _memory_manager.get()); - Tensor indices_tensor = makeInputTensor({4}, indices_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - GatherParams gparams; - - gparams.axis = 0; - gparams.batch_dims = 1; + test_kernel::NegTestDataWrongPositionTypeGatherKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} - Gather kernel(¶ms_tensor, &indices_tensor, &output_tensor, gparams); - EXPECT_ANY_THROW(kernel.configure()); +TEST_F(GatherTest, Wrong_axis_NEG) +{ + test_kernel::NegTestDataWrongAxisGatherKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } +// TODO: add S8 test } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Greater.cpp b/onert-micro/luci-interpreter/src/kernels/Greater.cpp index 68a0d47..b073a4a 100644 --- a/onert-micro/luci-interpreter/src/kernels/Greater.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Greater.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,128 +14,75 @@ * limitations under the License. */ -#include "kernels/Greater.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { +// TODO: reduce code duplication with less +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) +{ + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); -Greater::Greater(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); -void Greater::configure() + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::GreaterFn); +} + +} // namespace + +void configure_kernel_CircleGreater(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); } -void Greater::execute() const +void execute_kernel_CircleGreater(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (x()->element_type()) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + switch (Tensor::element_type(kernel.input1())) { - case DataType::FLOAT32: - evalFloat(); - break; case DataType::S64: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; case DataType::S32: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; - case DataType::U8: - evalQuantized(); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -void Greater::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreater(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::Greater(op_params, getTensorShape(x()), x_data, getTensorShape(y()), - y_data, getTensorShape(output()), output_data); - } -} - -template void Greater::evalInteger() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreaterNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::GreaterNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } -} - -void Greater::evalQuantized() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.left_shift = 8; - op_params.input1_offset = -x()->zero_point(); // Note the '-' - op_params.input1_shift = _x_shift; - op_params.input1_multiplier = _x_multiplier; - op_params.input2_offset = -y()->zero_point(); // Note the '-' - op_params.input2_shift = _y_shift; - op_params.input2_multiplier = _y_multiplier; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreaterWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::GreaterWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Greater.test.cpp b/onert-micro/luci-interpreter/src/kernels/Greater.test.cpp index a480801..cd938f0 100644 --- a/onert-micro/luci-interpreter/src/kernels/Greater.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Greater.test.cpp @@ -15,14 +15,13 @@ * limitations under the License. */ -#include "kernels/Greater.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/greater/FloatGreaterKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,305 +29,61 @@ using namespace testing; class GreaterTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(GreaterTest, FloatSimple) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, false, true, // Row 1 - true, false, false, // Row 2 - }; - - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(GreaterTest, FloatBroardcast) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; - - std::vector ref_output_data{ - false, false, true, // Row 1 - true, false, false, // Row 2 - false, false, true, // Row 3 - }; - - Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); -} - -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +template +std::vector checkGreaterKernel(test_kernel::TestDataBase *test_data_base) { - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value + 1, -2, max_value}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector ref_output_data{false, true, false}; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); -} - -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -4, -3, // Row 3 - min_value, -2, max_value, // Row 4 - }; + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - std::vector y_data{ - min_value + 1, -2, max_value - 1, // Row 1 - }; + runtime_module.execute(); - std::vector ref_output_data{ - false, true, false, // Row 1 - true, true, true, // Row 2 - true, false, false, // Row 3 - false, false, true, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -TEST_F(GreaterTest, Int32) -{ - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(GreaterTest, Int64) +TEST_F(GreaterTest, FloatNoBroadcast_P) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatGreater test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkGreaterKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(GreaterTest, Uint8Quantized) +TEST_F(GreaterTest, FloatNoBroadcast_NEG) { - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, false, true, true, // Row 1 - true, false, true, false, // Row 2 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(GreaterTest, Uint8QuantizedRescale) -{ - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, false, true, true, // Row 1 - true, false, true, false, // Row 2 - }; - - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 3); - - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(GreaterTest, Uint8QuantizedBroadcast) -{ - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - true, false, true, false, // Row 1 - true, true, false, false, // Row 2 - true, false, true, false, // Row 3 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(GreaterTest, Input_Type_Mismatch_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterTest, Input_Output_Type_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterTest, Float_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterTest, Int32_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterTest, Int64_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Greater kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatGreater test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkGreaterKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.cpp b/onert-micro/luci-interpreter/src/kernels/GreaterEqual.cpp index c2a7dce..dfb585d 100644 --- a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.cpp +++ b/onert-micro/luci-interpreter/src/kernels/GreaterEqual.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,131 +14,77 @@ * limitations under the License. */ -#include "kernels/GreaterEqual.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { - -GreaterEqual::GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output) - : Kernel({x, y}, {output}) +// TODO: reduce code duplication with less +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) { + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); + + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); + + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::GreaterEqualFn); } -void GreaterEqual::configure() +} // namespace + +void configure_kernel_CircleGreaterEqual(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); } -void GreaterEqual::execute() const +void execute_kernel_CircleGreaterEqual(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - switch (x()->element_type()) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + switch (Tensor::element_type(kernel.input1())) { - case DataType::FLOAT32: - evalFloat(); - break; case DataType::S64: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; case DataType::S32: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; - case DataType::U8: - evalQuantized(); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -void GreaterEqual::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreaterEqual(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::GreaterEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), - y_data, getTensorShape(output()), output_data); - } -} - -template void GreaterEqual::evalInteger() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreaterEqualNoScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } - else - { - tflite::reference_ops::GreaterEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } -} - -void GreaterEqual::evalQuantized() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.left_shift = 8; - op_params.input1_offset = -x()->zero_point(); // Note the '-' - op_params.input1_shift = _x_shift; - op_params.input1_multiplier = _x_multiplier; - op_params.input2_offset = -y()->zero_point(); // Note the '-' - op_params.input2_shift = _y_shift; - op_params.input2_multiplier = _y_multiplier; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowGreaterEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } - else - { - tflite::reference_ops::GreaterEqualWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.h b/onert-micro/luci-interpreter/src/kernels/GreaterEqual.h deleted file mode 100644 index 64f4f4c..0000000 --- a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H -#define LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class GreaterEqual : public Kernel -{ -public: - GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output); - - const Tensor *x() const { return _inputs[0]; } - const Tensor *y() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; - -private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H diff --git a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp b/onert-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp index 35bf88e..4bc2e2b 100644 --- a/onert-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/GreaterEqual.test.cpp @@ -15,14 +15,13 @@ * limitations under the License. */ -#include "kernels/GreaterEqual.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/greater_equal/FloatGreaterEqualKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,304 +29,61 @@ using namespace testing; class GreaterEqualTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(GreaterEqualTest, FloatSimple) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, true, true, // Row 1 - true, true, false, // Row 2 - }; - - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(GreaterEqualTest, FloatBroardcast) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; - - std::vector ref_output_data{ - false, true, true, // Row 1 - true, false, false, // Row 2 - false, false, true, // Row 3 - }; - - Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); -} -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value + 1, -2, max_value}; - - std::vector ref_output_data{false, true, true}; - - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); -} - -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -4, -3, // Row 3 - min_value, -2, max_value - 1, // Row 4 - }; - - std::vector y_data{ - min_value + 1, -2, max_value - 1, // Row 1 - }; - - std::vector ref_output_data{ - false, true, false, // Row 1 - true, true, true, // Row 2 - true, false, false, // Row 3 - false, true, true, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} - -TEST_F(GreaterEqualTest, Int32) -{ - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(GreaterEqualTest, Int64) +template +std::vector checkGreaterEqualKernel(test_kernel::TestDataBase *test_data_base) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); -} - -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(GreaterEqualTest, Uint8Quantized) -{ - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - false, true, true, true, // Row 1 - true, false, true, false, // Row 2 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(GreaterEqualTest, Uint8QuantizedRescale) -{ - std::vector x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - std::vector y_data{ - 0.9, 0.5, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - std::vector ref_output_data{ - false, true, true, true, // Row 1 - true, false, true, false, // Row 2 - }; + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); + runtime_module.execute(); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(GreaterEqualTest, Uint8QuantizedBroadcast) +TEST_F(GreaterEqualTest, FloatNoBroadcast_P) { - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - true, false, true, false, // Row 1 - true, true, true, false, // Row 2 - true, false, true, false, // Row 3 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatGreaterEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkGreaterEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(GreaterEqualTest, Input_Type_Mismatch_NEG) +TEST_F(GreaterEqualTest, FloatNoBroadcast_NEG) { - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterEqualTest, Input_Output_Type_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterEqualTest, Float_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterEqualTest, Int32_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(GreaterEqualTest, Int64_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatGreaterEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkGreaterEqualKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/If.cpp b/onert-micro/luci-interpreter/src/kernels/If.cpp index b472323..971708b 100644 --- a/onert-micro/luci-interpreter/src/kernels/If.cpp +++ b/onert-micro/luci-interpreter/src/kernels/If.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/If.h b/onert-micro/luci-interpreter/src/kernels/If.h index 22bb2dc..fa6ab37 100644 --- a/onert-micro/luci-interpreter/src/kernels/If.h +++ b/onert-micro/luci-interpreter/src/kernels/If.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.cpp b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.cpp index dc0fec9..577dc64 100644 --- a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.cpp +++ b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.h b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.h index 77922e7..a70a84e 100644 --- a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.h +++ b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp index 976f1b5..04400c3 100644 --- a/onert-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/InstanceNorm.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/KernelBuilder.cpp b/onert-micro/luci-interpreter/src/kernels/KernelBuilder.cpp index 4b2e662..5d1e885 100644 --- a/onert-micro/luci-interpreter/src/kernels/KernelBuilder.cpp +++ b/onert-micro/luci-interpreter/src/kernels/KernelBuilder.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2023 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. @@ -16,29 +15,13 @@ */ #include "KernelBuilder.h" -#include "Builders.h" namespace luci_interpreter { -KernelConfigureRegistry::KernelConfigureRegistry() -{ -#define REGISTER_KERNEL(builtin_operator, name) \ - register_kernel_configure(circle::BuiltinOperator::BuiltinOperator_##builtin_operator, \ - configure_kernel_Circle##name); - -#if USE_GENERATED_LIST -#include "GeneratedKernelsToBuild.lst" -#else -#include "KernelsToBuild.lst" -#endif - -#undef REGISTER_KERNEL -} - void KernelConfigureRegistry::configure_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, - BaseRuntimeGraph *runtime_graph) + BaseRuntimeGraph *runtime_graph) const { auto specific_configure_func = get_kernel_configure_func(opcode); if (specific_configure_func == nullptr) @@ -47,30 +30,15 @@ void KernelConfigureRegistry::configure_kernel(const circle::Operator *cur_op, specific_configure_func(cur_op, runtime_graph); } -KernelExecuteRegistry::KernelExecuteRegistry() -{ -#define REGISTER_KERNEL(builtin_operator, name) \ - register_kernel_execute(circle::BuiltinOperator::BuiltinOperator_##builtin_operator, \ - execute_kernel_Circle##name); - -#if USE_GENERATED_LIST -#include "GeneratedKernelsToBuild.lst" -#else -#include "KernelsToBuild.lst" -#endif - -#undef REGISTER_KERNEL -} - void KernelExecuteRegistry::execute_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, - BaseRuntimeGraph *runtime_graph, bool is_inplace) + BaseRuntimeGraph *runtime_graph) const { auto specific_execute_func = get_kernel_execute_func(opcode); if (specific_execute_func == nullptr) assert(false && "Unsupported operator"); - specific_execute_func(cur_op, runtime_graph, is_inplace); + specific_execute_func(cur_op, runtime_graph); } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/KernelBuilder.h b/onert-micro/luci-interpreter/src/kernels/KernelBuilder.h index 06da653..3d10f49 100644 --- a/onert-micro/luci-interpreter/src/kernels/KernelBuilder.h +++ b/onert-micro/luci-interpreter/src/kernels/KernelBuilder.h @@ -1,6 +1,5 @@ /* * 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. @@ -18,73 +17,135 @@ #ifndef LUCI_INTERPRETER_KERNEL_KERNELBUILDER_H #define LUCI_INTERPRETER_KERNEL_KERNELBUILDER_H -#include "core/RuntimeGraph.h" +#include "core/RuntimeModule.h" #include "luci_interpreter/core/reader/CircleMicroReader.h" +#include "Builders.h" #include #include namespace luci_interpreter { -namespace +#define REGISTER_KERNEL(builtin_operator, name) BuiltinOperator_##builtin_operator, + +enum class BuilderID +{ +#if USE_GENERATED_LIST +#include "GeneratedKernelsToBuild.lst" +#else +#include "KernelsToBuild.lst" +#endif + Size // casts to count of values in BuilderId enum +}; + +#undef REGISTER_KERNEL + +constexpr BuilderID get_builder_id(circle::BuiltinOperator opcode) { -#ifdef USE_STATIC_ALLOC -using BaseRuntimeGraph = StaticRuntimeGraph; + switch (opcode) + { +#define REGISTER_KERNEL(builtin_operator, name) \ + case circle::BuiltinOperator_##builtin_operator: \ + return BuilderID::BuiltinOperator_##builtin_operator; + +#if USE_GENERATED_LIST +#include "GeneratedKernelsToBuild.lst" #else -using BaseRuntimeGraph = RuntimeGraph; +#include "KernelsToBuild.lst" #endif -} // namespace + +#undef REGISTER_KERNEL + default: + assert(false && "Unsupported operation"); + } +} class KernelConfigureRegistry { public: using KernelConfigureFunc = void(const circle::Operator *, BaseRuntimeGraph *); - KernelConfigureRegistry(); + constexpr KernelConfigureRegistry() : _operator_configure() + { +#define REGISTER_KERNEL(builtin_operator, name) \ + register_kernel_configure(BuilderID::BuiltinOperator_##builtin_operator, \ + configure_kernel_Circle##name); - void configure_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, - BaseRuntimeGraph *runtime_graph); +#if USE_GENERATED_LIST +#include "GeneratedKernelsToBuild.lst" +#else +#include "KernelsToBuild.lst" +#endif -private: - std::unordered_map _operator_configure; +#undef REGISTER_KERNEL + } + + void configure_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, + BaseRuntimeGraph *runtime_graph) const; private: - KernelConfigureFunc *get_kernel_configure_func(circle::BuiltinOperator opcode) const + constexpr KernelConfigureFunc *get_kernel_configure_func(circle::BuiltinOperator opcode) const { - return _operator_configure.at(size_t(opcode)); + const auto builder_id_opcode = size_t(get_builder_id(opcode)); + assert(builder_id_opcode < size_t(BuilderID::Size)); + return _operator_configure[builder_id_opcode]; } - void register_kernel_configure(circle::BuiltinOperator id, KernelConfigureFunc *func) + constexpr void register_kernel_configure(BuilderID id, KernelConfigureFunc *func) { + assert(size_t(id) < size_t(BuilderID::Size)); _operator_configure[size_t(id)] = func; } + +private: + KernelConfigureFunc *_operator_configure[size_t(BuilderID::Size)]; }; class KernelExecuteRegistry { public: - using KernelExecuteFunc = void(const circle::Operator *, BaseRuntimeGraph *, bool); + using KernelExecuteFunc = void(const circle::Operator *, BaseRuntimeGraph *); - KernelExecuteRegistry(); + constexpr KernelExecuteRegistry() : _operator_execute() + { +#define REGISTER_KERNEL(builtin_operator, name) \ + register_kernel_execute(BuilderID::BuiltinOperator_##builtin_operator, \ + execute_kernel_Circle##name); - void execute_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, - BaseRuntimeGraph *runtime_graph, bool is_inplace); +#if USE_GENERATED_LIST +#include "GeneratedKernelsToBuild.lst" +#else +#include "KernelsToBuild.lst" +#endif -private: - std::unordered_map _operator_execute; +#undef REGISTER_KERNEL + } + + void execute_kernel(const circle::Operator *cur_op, circle::BuiltinOperator opcode, + BaseRuntimeGraph *runtime_graph) const; private: - KernelExecuteFunc *get_kernel_execute_func(circle::BuiltinOperator opcode) const + constexpr KernelExecuteFunc *get_kernel_execute_func(circle::BuiltinOperator opcode) const { - return _operator_execute.at(size_t(opcode)); + const auto tmp = size_t(get_builder_id(opcode)); + assert(tmp < size_t(BuilderID::Size)); + return _operator_execute[tmp]; } - void register_kernel_execute(circle::BuiltinOperator id, KernelExecuteFunc *func) + constexpr void register_kernel_execute(BuilderID id, KernelExecuteFunc *func) { + assert(size_t(id) < size_t(BuilderID::Size)); _operator_execute[size_t(id)] = func; } + +private: + KernelExecuteFunc *_operator_execute[size_t(BuilderID::Size)]; }; +// Global constexpr kernel configure and kernel executor +constexpr KernelConfigureRegistry kernel_configure; +constexpr KernelExecuteRegistry kernel_executor; + } // namespace luci_interpreter #endif // LUCI_INTERPRETER_KERNEL_KERNELBUILDER_H diff --git a/onert-micro/luci-interpreter/src/kernels/L2Normalize.cpp b/onert-micro/luci-interpreter/src/kernels/L2Normalize.cpp index 09482a5..97c9db8 100644 --- a/onert-micro/luci-interpreter/src/kernels/L2Normalize.cpp +++ b/onert-micro/luci-interpreter/src/kernels/L2Normalize.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/L2Normalize.h b/onert-micro/luci-interpreter/src/kernels/L2Normalize.h index 9c1716b..6c7dac6 100644 --- a/onert-micro/luci-interpreter/src/kernels/L2Normalize.h +++ b/onert-micro/luci-interpreter/src/kernels/L2Normalize.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h index 4d1ed5d..d40f5f4 100644 --- a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h +++ b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.cpp b/onert-micro/luci-interpreter/src/kernels/LeakyRelu.cpp index ab7072f..7f032b5 100644 --- a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LeakyRelu.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,76 +14,66 @@ * limitations under the License. */ -#include "kernels/LeakyRelu.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include - -#include "PALLeakyRelu.h" +#include "PALReluCommon.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleLeakyRelu(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { + kernels::SISOKernel kernel(cur_op, runtime_graph); -LeakyRelu::LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams ¶ms) - : KernelWithParams({input}, {output}, params) -{ + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) == Tensor::num_dims(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input()) == + Tensor::num_elements(kernel.output())); } -void LeakyRelu::configure() +void execute_kernel_CircleLeakyRelu(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); - if (input()->element_type() == DataType::U8) - { - double alpha_multiplier = input()->scale() * params().alpha / output()->scale(); - quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha); - double identity_multiplier = input()->scale() / output()->scale(); - quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); -} + kernels::SISOKernel kernel(cur_op, runtime_graph); -void LeakyRelu::execute() const -{ - switch (input()->element_type()) + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); + + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + const auto options = cur_op->builtin_options_as_LeakyReluOptions(); + + switch (Tensor::element_type(kernel.input())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); - break; - case DataType::U8: - evalQuantized(); + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::ReLUCommon(flat_size, input_data_float, output_data_float, + options->alpha(), false); break; + } +#endif // DIS_FLOAT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -} - -void LeakyRelu::evalFloat() const -{ - tflite::LeakyReluParams op_params{}; - op_params.alpha = params().alpha; - luci_interpreter_pal::LeakyRelu(op_params, getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); -} -void LeakyRelu::evalQuantized() const -{ - tflite::LeakyReluParams op_params{}; - op_params.input_offset = input()->zero_point(); - op_params.output_offset = output()->zero_point(); - op_params.output_multiplier_alpha = _output_multiplier_alpha; - op_params.output_shift_alpha = _output_shift_alpha; - op_params.output_multiplier_identity = _output_multiplier_identity; - op_params.output_shift_identity = _output_shift_identity; - - tflite::reference_ops::QuantizeLeakyRelu( - op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); } - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.h b/onert-micro/luci-interpreter/src/kernels/LeakyRelu.h deleted file mode 100644 index 88e8c03..0000000 --- a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_LEAKYRELU_H -#define LUCI_INTERPRETER_KERNELS_LEAKYRELU_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class LeakyRelu : public KernelWithParams -{ -public: - LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams ¶ms); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - void evalQuantized() const; - -private: - int32_t _output_multiplier_alpha = 0; - int _output_shift_alpha = 0; - int32_t _output_multiplier_identity = 0; - int _output_shift_identity = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_LEAKYRELU_H diff --git a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp b/onert-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp index 6063fa5..049ba21 100644 --- a/onert-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LeakyRelu.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,114 +14,74 @@ * limitations under the License. */ -#include "kernels/LeakyRelu.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/leaky_relu/FloatLeakyReLUKernel.h" +#include "luci_interpreter/test_models/leaky_relu/NegLeakyReLUKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template -void Check(std::initializer_list input_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list output_data, - float alpha) +class LeakyReLUTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(element_type); - - LeakyReluParams params{}; - params.alpha = alpha; - - LeakyRelu kernel(&input_tensor, &output_tensor, params); - - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); -} + // Do nothing +}; -template <> -void Check(std::initializer_list input_shape, - std::initializer_list output_shape, - std::initializer_list input_data, - std::initializer_list output_data, float alpha) +template +std::vector checkLeakyReLUKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - const float quantized_tolerance = getTolerance(-8, 127.f / 16.f, 255); - std::pair quant_param = quantizationParams(-8, 127.f / 16.f); - Tensor input_tensor = makeInputTensor( - input_shape, quant_param.first, quant_param.second, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - LeakyReluParams params{}; - params.alpha = alpha; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - LeakyRelu kernel(&input_tensor, &output_tensor, params); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data, quantized_tolerance)); -} + runtime_module.execute(); -template class LeakReluTest : public ::testing::Test -{ -}; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(LeakReluTest, DataTypes); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} -TYPED_TEST(LeakReluTest, Simple) +TEST_F(LeakyReLUTest, Float_P) { - Check(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, - /*input_data=*/ - { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 - }, - /*output_data=*/ - { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -0.5f, -1.0f, // Row 2 - }, - /*alpha=*/0.5f); - - SUCCEED(); + test_kernel::TestDataFloatLeakyReLU test_data_kernel; + std::vector output_data_vector = checkLeakyReLUKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST(LeakReluTest, IvalidInputOutputType_NEG) +TEST_F(LeakyReLUTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input_tensor = makeInputTensor({2, 3}, - { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 - }, - memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - LeakyReluParams params{}; - params.alpha = 0.5f; - - LeakyRelu kernel(&input_tensor, &output_tensor, params); - - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchLeakyReLUKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Less.cpp b/onert-micro/luci-interpreter/src/kernels/Less.cpp index 54a785c..4a688e4 100644 --- a/onert-micro/luci-interpreter/src/kernels/Less.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Less.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,128 +14,140 @@ * limitations under the License. */ -#include "kernels/Less.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { +#ifndef DIS_QUANT +void evalQuantized(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) +{ + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); -Less::Less(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + assert(x_data != nullptr); -void Less::configure() -{ - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); -} + assert(y_data != nullptr); -void Less::execute() const -{ - switch (x()->element_type()) - { - case DataType::FLOAT32: - evalFloat(); - break; - case DataType::S64: - evalInteger(); - break; - case DataType::S32: - evalInteger(); - break; - case DataType::U8: - evalQuantized(); - break; - default: - assert(false && "Unsupported type."); - } -} + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); -void Less::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); + int32_t x_multiplier; + int x_shift; + + int32_t y_multiplier; + int y_shift; - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); + kernels::quantizeMultiplierSmallerThanOneExp(Tensor::scale(x), &x_multiplier, &x_shift); + kernels::quantizeMultiplierSmallerThanOneExp(Tensor::scale(y), &y_multiplier, &y_shift); + + luci_interpreter_pal::ComparisonParams op_params; + op_params.left_shift = 8; + op_params.input1_offset = -Tensor::zero_point(x); // Note the '-' + op_params.input1_shift = x_shift; + op_params.input1_multiplier = x_multiplier; + op_params.input2_offset = -Tensor::zero_point(y); // Note the '-' + op_params.input2_shift = y_shift; + op_params.input2_multiplier = y_multiplier; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); if (op_params.is_broadcast) { - tflite::reference_ops::Broadcast4DSlowLess(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + luci_interpreter_pal::BroadcastComparison4DSlowWithScaling( + op_params, kernels::getTensorShape(x), x_data, kernels::getTensorShape(y), y_data, + kernels::getTensorShape(output), output_data, luci_interpreter_pal::LessFn); } else { - tflite::reference_ops::Less(op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonWithScaling(op_params, flat_size, x_data, y_data, + output_data, luci_interpreter_pal::LessFn); } } +#endif // DIS_QUANT -template void Less::evalInteger() const +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) { - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); + + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); if (op_params.is_broadcast) { - tflite::reference_ops::Broadcast4DSlowLessNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + luci_interpreter_pal::BroadcastComparison4DSlowNoScaling( + op_params, kernels::getTensorShape(x), x_data, kernels::getTensorShape(y), y_data, + kernels::getTensorShape(output), output_data, luci_interpreter_pal::LessFn); } else { - tflite::reference_ops::LessNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::LessFn); } } -void Less::evalQuantized() const +} // namespace + +void configure_kernel_CircleLess(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); + kernels::TISOKernel kernel(cur_op, runtime_graph); - 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(); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); +} - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowLessWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else +void execute_kernel_CircleLess(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); + + switch (Tensor::element_type(kernel.input1())) { - tflite::reference_ops::LessWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); + case DataType::S64: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; + case DataType::S32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; +#ifndef DIS_QUANT + case DataType::U8: + evalQuantized(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; +#endif // DIS_QUANT +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Less.test.cpp b/onert-micro/luci-interpreter/src/kernels/Less.test.cpp index 8c59633..08279e9 100644 --- a/onert-micro/luci-interpreter/src/kernels/Less.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Less.test.cpp @@ -15,14 +15,16 @@ * limitations under the License. */ -#include "kernels/Less.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/less/FloatLessKernel.h" +#include "luci_interpreter/test_models/less/IntLessKernel.h" +#include "luci_interpreter/test_models/less/QuantLessKernel.h" +#include "luci_interpreter/test_models/less/NegTestDataLessKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,305 +32,134 @@ using namespace testing; class LessTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(LessTest, FloatSimple) +template +std::vector checkLessKernel(test_kernel::TestDataBase *test_data_base) { - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - std::vector ref_output_data{ - true, false, false, // Row 1 - false, false, true, // Row 2 - }; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(LessTest, FloatBroardcast) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - }; + runtime_module.execute(); - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - std::vector ref_output_data{ - true, false, false, // Row 1 - false, true, true, // Row 2 - true, true, false, // Row 3 - }; - - Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +TEST_F(LessTest, FloatNoBroadcast_P) { - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value + 1, -2, max_value}; - - std::vector ref_output_data{true, false, false}; - - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatLess test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) +TEST_F(LessTest, FloatWithBroadcast_P) { - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -4, -3, // Row 3 - min_value, -2, max_value, // Row 4 - }; - - std::vector y_data{ - min_value + 1, -2, max_value - 1, // Row 1 - }; - - std::vector ref_output_data{ - true, false, true, // Row 1 - false, false, false, // Row 2 - false, true, true, // Row 3 - true, false, false, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); + const bool is_with_broadcast = true; + test_kernel::TestDataFloatLess test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LessTest, Int32) +TEST_F(LessTest, FloatNoBroadcast_NEG) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatLess test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessKernel(&test_data_kernel), ""); } -TEST_F(LessTest, Int64) +TEST_F(LessTest, FloatWithBroadcast_NEG) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = true; + test_kernel::TestDataFloatLess test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessKernel(&test_data_kernel), ""); } -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(LessTest, Uint8Quantized) +TEST_F(LessTest, IntWithBroadcast_P) { - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, false, false, false, // Row 1 - false, true, false, true, // Row 2 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + const bool is_with_broadcast = true; + test_kernel::TestDataIntLess test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LessTest, Uint8QuantizedRescale) +TEST_F(LessTest, IntNoBroadcast_P) { - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, false, false, false, // Row 1 - false, true, false, true, // Row 2 - }; - - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); - - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); + const bool is_with_broadcast = false; + test_kernel::TestDataIntLess test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LessTest, Uint8QuantizedBroadcast) +TEST_F(LessTest, IntWithBroadcast_NEG) { - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - false, true, false, true, // Row 1 - false, false, false, true, // Row 2 - false, true, false, true, // Row 3 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(LessTest, Input_Type_Mismatch_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = true; + test_kernel::TestDataIntLess test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessKernel(&test_data_kernel), ""); } -TEST_F(LessTest, Input_Output_Type_NEG) +TEST_F(LessTest, IntNoBroadcast_NEG) { - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataIntLess test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessKernel(&test_data_kernel), ""); } -TEST_F(LessTest, Float_Broadcast_NEG) +TEST_F(LessTest, Quant_P) { - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataQuantLess test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LessTest, Int32_Broadcast_NEG) +TEST_F(LessTest, Quant_NEG) { - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - Less kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataQuantLess test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessKernel(&test_data_kernel), ""); } -TEST_F(LessTest, Int64_Broadcast_NEG) +TEST_F(LessTest, Wrong_Output_Type_NEG) { - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + test_kernel::NegTestDataLessKernel test_data_kernel; - Less kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LessEqual.cpp b/onert-micro/luci-interpreter/src/kernels/LessEqual.cpp index 03b122a..8928ba4e 100644 --- a/onert-micro/luci-interpreter/src/kernels/LessEqual.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LessEqual.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,128 +14,76 @@ * limitations under the License. */ -#include "kernels/LessEqual.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { +// TODO: reduce code duplication with less +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) +{ + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); -LessEqual::LessEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); -void LessEqual::configure() + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::LessEqualFn); +} + +} // namespace + +void configure_kernel_CircleLessEqual(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); } -void LessEqual::execute() const +void execute_kernel_CircleLessEqual(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (x()->element_type()) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + switch (Tensor::element_type(kernel.input1())) { - case DataType::FLOAT32: - evalFloat(); - break; case DataType::S64: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; case DataType::S32: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; - case DataType::U8: - evalQuantized(); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -void LessEqual::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowLessEqual(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::LessEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), - y_data, getTensorShape(output()), output_data); - } -} - -template void LessEqual::evalInteger() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowLessEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::LessEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } -} - -void LessEqual::evalQuantized() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.left_shift = 8; - op_params.input1_offset = -x()->zero_point(); // Note the '-' - op_params.input1_shift = _x_shift; - op_params.input1_multiplier = _x_multiplier; - op_params.input2_offset = -y()->zero_point(); // Note the '-' - op_params.input2_shift = _y_shift; - op_params.input2_multiplier = _y_multiplier; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowLessEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } - else - { - tflite::reference_ops::LessEqualWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LessEqual.h b/onert-micro/luci-interpreter/src/kernels/LessEqual.h deleted file mode 100644 index 93da856..0000000 --- a/onert-micro/luci-interpreter/src/kernels/LessEqual.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H -#define LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class LessEqual : public Kernel -{ -public: - LessEqual(const Tensor *x, const Tensor *y, Tensor *output); - - const Tensor *x() const { return _inputs[0]; } - const Tensor *y() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; - -private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H diff --git a/onert-micro/luci-interpreter/src/kernels/LessEqual.test.cpp b/onert-micro/luci-interpreter/src/kernels/LessEqual.test.cpp index b2e2fa7..e53dc05 100644 --- a/onert-micro/luci-interpreter/src/kernels/LessEqual.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LessEqual.test.cpp @@ -15,14 +15,13 @@ * limitations under the License. */ -#include "kernels/LessEqual.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/less_equal/FloatLessEqualKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,305 +29,61 @@ using namespace testing; class LessEqualTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(LessEqualTest, FloatSimple) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, true, false, // Row 1 - false, true, true, // Row 2 - }; - - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(LessEqualTest, FloatBroardcast) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; - - std::vector ref_output_data{ - true, true, false, // Row 1 - false, true, true, // Row 2 - true, true, false, // Row 3 - }; - - Tensor x_tensor = makeInputTensor({3, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3})); -} - -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) +template +std::vector checkLessEqualKernel(test_kernel::TestDataBase *test_data_base) { - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value + 1, -2, max_value}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector ref_output_data{true, false, true}; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); -} - -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -4, -3, // Row 3 - min_value, -2, max_value, // Row 4 - }; + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - std::vector y_data{ - min_value + 1, -2, max_value - 1, // Row 1 - }; + runtime_module.execute(); - std::vector ref_output_data{ - true, false, true, // Row 1 - false, false, false, // Row 2 - false, true, true, // Row 3 - true, true, false, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -TEST_F(LessEqualTest, Int32) -{ - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(LessEqualTest, Int64) +TEST_F(LessEqualTest, FloatNoBroadcast_P) { - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatLessEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkLessEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(LessEqualTest, Uint8Quantized) +TEST_F(LessEqualTest, FloatNoBroadcast_NEG) { - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, true, false, false, // Row 1 - false, true, false, true, // Row 2 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(LessEqualTest, Uint8QuantizedRescale) -{ - std::vector x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, true, false, false, // Row 1 - false, true, false, true, // Row 2 - }; - - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - std::pair y_quant_param = quantizationParams(F_MIN * 1.2, F_MAX * 1.5); - - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(LessEqualTest, Uint8QuantizedBroadcast) -{ - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - false, true, false, true, // Row 1 - false, false, true, true, // Row 2 - false, true, false, true, // Row 3 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 3, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(LessEqualTest, Input_Type_Mismatch_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(LessEqualTest, Input_Output_Type_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(LessEqualTest, Float_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); -} - -TEST_F(LessEqualTest, Int32_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); -} - -TEST_F(LessEqualTest, Int64_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatLessEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkLessEqualKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp b/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp index 49a9eee..bf08db0 100644 --- a/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h b/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h index 16b6ba4..60408a1 100644 --- a/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h +++ b/onert-micro/luci-interpreter/src/kernels/LocalResponseNormalization.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LogSoftmax.cpp b/onert-micro/luci-interpreter/src/kernels/LogSoftmax.cpp index d5f9917..b467cb0 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogSoftmax.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogSoftmax.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LogSoftmax.h b/onert-micro/luci-interpreter/src/kernels/LogSoftmax.h index 26c5dc3..18477fb 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogSoftmax.h +++ b/onert-micro/luci-interpreter/src/kernels/LogSoftmax.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.cpp b/onert-micro/luci-interpreter/src/kernels/LogicalAnd.cpp index 65e0b50..7e440cc 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogicalAnd.cpp @@ -14,50 +14,58 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "kernels/LogicalAnd.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include "kernels/BinaryOpCommon.h" +#include "PALLogicalCommon.h" namespace luci_interpreter { -namespace kernels +namespace { +bool LogicalAnd(bool x, bool y) { return x && y; } +} // namespace -LogicalAnd::LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) +void configure_kernel_CircleLogicalAnd(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { -} + kernels::TISOKernel kernel(cur_op, runtime_graph); -void LogicalAnd::configure() -{ - LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); - LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); -} + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == DataType::BOOL); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); -void LogicalAnd::execute() const -{ - switch (input1()->element_type()) - { - case DataType::BOOL: - evalLogicalAnd(); - break; - default: - assert(false && "Unsupported type."); - } + // TODO support broadcast + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input1()) == + Tensor::num_elements(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input1()) == Tensor::num_dims(kernel.input2())); } -inline void LogicalAnd::evalLogicalAnd() const +// TODO: add inplace +// TODO: reduce code duplication with LogicalOr +void execute_kernel_CircleLogicalAnd(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output()), - [](bool x, bool y) { return x && y; }); + kernels::TISOKernel kernel(cur_op, runtime_graph); + + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.input1())); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(kernel.input1())); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.input2())); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(kernel.input2())); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.output())); + + const int64_t flat_size = kernels::getTensorShape(kernel.input1()).flatSize(); + luci_interpreter_pal::LogicalCommon(flat_size, x_data, y_data, output_data, LogicalAnd); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.h b/onert-micro/luci-interpreter/src/kernels/LogicalAnd.h deleted file mode 100644 index 2609b31..0000000 --- a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_LOGICALAND_H -#define LUCI_INTERPRETER_KERNELS_LOGICALAND_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class LogicalAnd : public Kernel -{ -public: - LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - inline void evalLogicalAnd() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_LOGICALAND_H diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp b/onert-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp index 21b7951..2f72c08 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogicalAnd.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/LogicalAnd.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/logical_and/BoolLogicalAndKernel.h" +#include "luci_interpreter/test_models/logical_and/NegLogicalAndKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,72 +30,66 @@ using namespace testing; class LogicalAndTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(LogicalAndTest, Basic) +template +std::vector checkLogicalAndKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 1, 1, 4}; - Tensor input_tensor1 = - makeInputTensor(input_shape, {true, false, false, true}, _memory_manager.get()); - Tensor input_tensor2 = - makeInputTensor(input_shape, {true, false, true, false}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAre(true, false, false, false)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); + + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(LogicalAndTest, Broadcast) +TEST_F(LogicalAndTest, Bool_P) { - Tensor input_tensor1 = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, - _memory_manager.get()); - Tensor input_tensor2 = - makeInputTensor({1, 1, 1, 1}, {true}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAre(true, false, false, true)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); + test_kernel::TestDataBoolLogicalAnd test_data_kernel; + std::vector output_data_vector = checkLogicalAndKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LogicalAndTest, MismatchInputType_NEG) +TEST_F(LogicalAndTest, Input_type_mismatch_NEG) { - Tensor input1_tensor = - makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(LogicalAndTest, InputTypeInvalid_NEG) -{ - Tensor input1_tensor = - makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1, 1, 1, 1}, {0}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputTypeMismatchLogicalAndKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalNot.cpp b/onert-micro/luci-interpreter/src/kernels/LogicalNot.cpp index 22dc0d2..4ba4499 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogicalNot.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogicalNot.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalNot.h b/onert-micro/luci-interpreter/src/kernels/LogicalNot.h deleted file mode 100644 index 6c4cfc1..0000000 --- a/onert-micro/luci-interpreter/src/kernels/LogicalNot.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_LOGICALNOT_H -#define LUCI_INTERPRETER_KERNELS_LOGICALNOT_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class LogicalNot : public Kernel -{ -public: - LogicalNot(const Tensor *input, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - inline void evalLogicalNot() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_LOGICALNOT_H diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalOr.cpp b/onert-micro/luci-interpreter/src/kernels/LogicalOr.cpp index ecb8ee4..207c739 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogicalOr.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogicalOr.cpp @@ -14,37 +14,58 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "kernels/LogicalOr.h" - +#include "Builders.h" #include "kernels/Utils.h" -#include "kernels/BinaryOpCommon.h" +#include "TISOKernel.h" + +#include "PALLogicalCommon.h" namespace luci_interpreter { -namespace kernels -{ -LogicalOr::LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) +namespace { -} +bool LogicalOr(bool x, bool y) { return x || y; } +} // namespace -void LogicalOr::configure() +void configure_kernel_CircleLogicalOr(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); - LUCI_INTERPRETER_CHECK(input1()->element_type() == DataType::BOOL); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); + kernels::TISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == DataType::BOOL); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); + + // TODO support broadcast + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input1()) == + Tensor::num_elements(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input1()) == Tensor::num_dims(kernel.input2())); } -void LogicalOr::execute() const +// TODO: add inplace +// TODO: reduce code duplication with LogicalAnd +void execute_kernel_CircleLogicalOr(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output()), - [](bool x, bool y) { return x || y; }); + kernels::TISOKernel kernel(cur_op, runtime_graph); + + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.input1())); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(kernel.input1())); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.input2())); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(kernel.input2())); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(kernel.output())); + + const int64_t flat_size = kernels::getTensorShape(kernel.input1()).flatSize(); + luci_interpreter_pal::LogicalCommon(flat_size, x_data, y_data, output_data, LogicalOr); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalOr.h b/onert-micro/luci-interpreter/src/kernels/LogicalOr.h deleted file mode 100644 index 8860648..0000000 --- a/onert-micro/luci-interpreter/src/kernels/LogicalOr.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_LOGICALOR_H -#define LUCI_INTERPRETER_KERNELS_LOGICALOR_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class LogicalOr : public Kernel -{ -public: - LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_LOGICALOR_H diff --git a/onert-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp b/onert-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp index d65a69a..baa338d 100644 --- a/onert-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/LogicalOr.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/LogicalOr.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/logical_or/BoolLogicalOrKernel.h" +#include "luci_interpreter/test_models/logical_or/NegLogicalOrKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,75 +30,66 @@ using namespace testing; class LogicalOrTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(LogicalOrTest, Basic) -{ - Tensor input1_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, - _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, true, false}, - _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAre(true, false, true, true)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); -} - -TEST_F(LogicalOrTest, Broadcast) +template +std::vector checkLogicalOrKernel(test_kernel::TestDataBase *test_data_base) { - Tensor input1_tensor = makeInputTensor({1, 1, 1, 4}, {true, false, false, true}, - _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAre(true, false, false, true)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4)); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); + + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(LogicalOrTest, MismatchInputType_NEG) +TEST_F(LogicalOrTest, Bool_P) { - Tensor input1_tensor = - makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor({1, 1, 1, 1}, {false}, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::S32); - - LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataBoolLogicalOr test_data_kernel; + std::vector output_data_vector = checkLogicalOrKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(LogicalOrTest, InputTypeInvalid_NEG) +TEST_F(LogicalOrTest, Input_type_mismatch_NEG) { - Tensor input1_tensor = - makeInputTensor({1, 1, 1, 4}, {1, 0, 0, 1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1, 1, 1, 1}, {0}, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputTypeMismatchLogicalOrKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Logistic.cpp b/onert-micro/luci-interpreter/src/kernels/Logistic.cpp index 4e8cba8..4dbc153 100644 --- a/onert-micro/luci-interpreter/src/kernels/Logistic.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Logistic.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -17,59 +16,10 @@ #include "Builders.h" #include "kernels/Utils.h" - -#include +#include "PALLogistic.h" namespace luci_interpreter { -namespace -{ - -#ifndef DIS_FLOAT -void evalFloat(const circle::Tensor *input, const circle::Tensor *output, bool is_inplace, - BaseRuntimeGraph *runtime_graph) -{ - const float *input_data = reinterpret_cast(runtime_graph->getDataByTensor(input)); - float *output_data = reinterpret_cast(runtime_graph->getDataByTensor(output)); - - if (is_inplace) - { - output_data = const_cast(input_data); - } - - assert(input_data != nullptr); - assert(output_data != nullptr); - - tflite::reference_ops::Logistic(kernels::getTensorShape(input), input_data, - kernels::getTensorShape(output), output_data); - if (is_inplace) - { - runtime_graph->makeInplaceOperation(input, output); - } -} -#endif // DIS_FLOAT - -#ifndef DIS_QUANT -void evalQuantized(const circle::Tensor *input, const circle::Tensor *output, bool is_inplace, - BaseRuntimeGraph *runtime_graph) -{ - const int8_t *input_data = - reinterpret_cast(runtime_graph->getDataByTensor(input)); - int8_t *output_data = reinterpret_cast(runtime_graph->getDataByTensor(output)); - if (is_inplace) - output_data = const_cast(input_data); - - tflite::reference_ops::Logistic(kernels::getTensorShape(input), input_data, Tensor::scale(input), - Tensor::zero_point(input), kernels::getTensorShape(output), - output_data, Tensor::scale(output), Tensor::zero_point(output)); - if (is_inplace) - { - runtime_graph->makeInplaceOperation(input, output); - } -} -#endif // DIS_QUANT - -} // namespace void configure_kernel_CircleLogistic(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) @@ -96,8 +46,7 @@ void configure_kernel_CircleLogistic(const circle::Operator *cur_op, #endif // DIS_QUANT } -void execute_kernel_CircleLogistic(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, - bool is_inplace) +void execute_kernel_CircleLogistic(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); const auto output_index = cur_op->outputs()->operator[](0); @@ -111,21 +60,45 @@ void execute_kernel_CircleLogistic(const circle::Operator *cur_op, BaseRuntimeGr assert(input != nullptr); assert(output != nullptr); + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + const uint8_t *input_data = runtime_graph->getDataByTensor(input); + uint8_t *output_data = runtime_graph->getDataByTensor(output); + + if (is_inplace) + { + output_data = const_cast(input_data); + } + + assert(input_data != nullptr); + assert(output_data != nullptr); + + const int flat_size = kernels::getTensorRuntimeShape(input, runtime_graph).flatSize(); + switch (Tensor::element_type(input)) { #ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(input, output, is_inplace, runtime_graph); + luci_interpreter_pal::Logistic(flat_size, kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); break; #endif // DIS_FLOAT #ifndef DIS_QUANT case DataType::S8: - evalQuantized(input, output, is_inplace, runtime_graph); + luci_interpreter_pal::Logistic(flat_size, kernels::getTensorData(input_data), + Tensor::scale(input), Tensor::zero_point(input), + kernels::getTensorData(output_data), + Tensor::scale(output), Tensor::zero_point(output)); break; #endif // DIS_QUANT default: assert(false && "Unsupported type."); } + + if (is_inplace) + { + runtime_graph->makeInplaceOperation(input, output); + } } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Logistic.test.cpp b/onert-micro/luci-interpreter/src/kernels/Logistic.test.cpp index 78fa9ee..36e3470 100644 --- a/onert-micro/luci-interpreter/src/kernels/Logistic.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Logistic.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -14,138 +13,89 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/Logistic.h" + #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/logistic/FloatLogisticKernel.h" +#include "luci_interpreter/test_models/logistic/NegLogisticKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; +class LogisticTest : public ::testing::Test +{ + // Do nothing +}; + template -void Check(std::initializer_list input_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list output_data) +std::vector checkLogisticKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Tensor input_tensor = - makeInputTensor()>(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(getElementType()); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Logistic kernel(&input_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); -} + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -template <> -void Check(std::initializer_list input_shape, - std::initializer_list output_shape, - std::initializer_list input_data, - std::initializer_list output_data) -{ - std::unique_ptr memory_manager = std::make_unique(); - - std::pair input_quant_param = - quantizationParams(std::min(input_data), std::max(input_data)); - Tensor input_tensor = - makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, - input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0); - - Logistic kernel(&input_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data, output_tensor.scale() * 2)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); -} + runtime_module.execute(); -template class LogisticTest : public ::testing::Test -{ -}; + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(LogisticTest, DataTypes); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} -TYPED_TEST(LogisticTest, Simple) +TEST_F(LogisticTest, Float_P) { - Check( - {89}, {89}, - {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636, - -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000, - -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364, - -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727, - -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091, - -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455, - -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818, - -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818, - 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455, - 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091, - 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727, - 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364, - 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000, - 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636, - 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000}, - {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198, - 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786, - 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065, - 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576, - 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562, - 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805, - 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241, - 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759, - 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195, - 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438, - 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424, - 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935, - 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214, - 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802, - 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021}); + test_kernel::TestDataFloatLogistic test_data_kernel; + std::vector output_data_vector = checkLogisticKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(LogisticTest, IvalidInputOutputType_NEG) +TEST_F(LogisticTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - - Shape input_shape = {1}; - std::vector input_data{10}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0); - - Logistic kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchLogisticKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(LogisticTest, IvalidQuantParam_NEG) +TEST_F(LogisticTest, No_quant_params_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - Shape input_shape = {2}; - std::vector input_data{-10, 10}; - std::pair input_quant_param = quantizationParams(-10, 10); - Tensor input_tensor = - makeInputTensor(input_shape, input_quant_param.first, input_quant_param.second, - input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 255, 0); - - Logistic kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataNoQuantParamsLogisticKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } +// TODO: add S8 test } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/MISOKernel.h b/onert-micro/luci-interpreter/src/kernels/MISOKernel.h new file mode 100644 index 0000000..96d20af --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/MISOKernel.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_MISO_KERNEL_H +#define LUCI_INTERPRETER_KERNELS_MISO_KERNEL_H + +#include "Builders.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +// Multiple input single output kernel +class MISOKernel +{ +public: + MISOKernel(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) + { + const auto input1_index = cur_op->inputs()->operator[](0); + const auto input2_index = cur_op->inputs()->operator[](1); + const auto input3_index = cur_op->inputs()->operator[](2); + const auto input4_index = + cur_op->inputs()->size() == 4 ? cur_op->inputs()->operator[](3) : -1; // optional + + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input1_index != -1); + assert(input2_index != -1); + assert(input3_index != -1); + + assert(output_index != -1); + + _input1_tensor = runtime_graph->getCircleTensorByIndex(input1_index); + _input2_tensor = runtime_graph->getCircleTensorByIndex(input2_index); + _input3_tensor = runtime_graph->getCircleTensorByIndex(input3_index); + _output_tensor = runtime_graph->getCircleTensorByIndex(output_index); + + // optional + if (input4_index != -1) + _input4_tensor = runtime_graph->getCircleTensorByIndex(input4_index); + else + _input4_tensor = nullptr; + + assert(_input1_tensor != nullptr); + assert(_input2_tensor != nullptr); + assert(_input3_tensor != nullptr); + assert(_output_tensor != nullptr); + } + + const circle::Tensor *input1() const { return _input1_tensor; } + const circle::Tensor *input2() const { return _input2_tensor; } + const circle::Tensor *input3() const { return _input3_tensor; } + + const circle::Tensor *input4() const + { + assert(_input4_tensor != nullptr); + return _input4_tensor; + } + + const circle::Tensor *output() const { return _output_tensor; } + +private: + const circle::Tensor *_input1_tensor; + const circle::Tensor *_input2_tensor; + const circle::Tensor *_input3_tensor; + const circle::Tensor *_input4_tensor; // optional + const circle::Tensor *_output_tensor; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_MISO_KERNEL_H diff --git a/onert-micro/luci-interpreter/src/kernels/MaxPool2D.cpp b/onert-micro/luci-interpreter/src/kernels/MaxPool2D.cpp index f6b521d..d85e8a9 100644 --- a/onert-micro/luci-interpreter/src/kernels/MaxPool2D.cpp +++ b/onert-micro/luci-interpreter/src/kernels/MaxPool2D.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -18,143 +17,10 @@ #include "Builders.h" #include "kernels/Utils.h" - -#include -#include +#include "PALMaxPool2D.h" namespace luci_interpreter { - -namespace -{ - -#ifndef DIS_FLOAT - -void evalFloat(const circle::Tensor *input, const circle::Tensor *output, - const circle::Pool2DOptions *options, BaseRuntimeGraph *runtime_graph) -{ - const int32_t input_height = Tensor::dim(input, 1); - const int32_t input_width = Tensor::dim(input, 2); - - const int32_t output_height = kernels::computeOutputSize( - luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); - const int32_t output_width = kernels::computeOutputSize( - luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); - - const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, - options->filter_height(), output_height); - const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, - options->filter_width(), output_width); - - const auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); - - float activation_min{}; - float activation_max{}; - kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), - &activation_min, &activation_max); - tflite::PoolParams params{}; - params.padding_values.height = padding_height; - params.padding_values.width = padding_width; - params.stride_height = options->stride_h(); - params.stride_width = options->stride_w(); - params.filter_height = options->filter_height(); - params.filter_width = options->filter_width(); - params.float_activation_min = activation_min; - params.float_activation_max = activation_max; - - tflite::reference_ops::MaxPool( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); -} - -#endif // DIS_FLOAT - -#ifndef DIS_QUANT -void evalQuantized(const circle::Tensor *input, const circle::Tensor *output, - const circle::Pool2DOptions *options, BaseRuntimeGraph *runtime_graph) -{ - int32_t activation_min{}; - int32_t activation_max{}; - kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), - output, &activation_min, &activation_max); - - // Compute padding - const int32_t input_height = Tensor::dim(input, 1); - const int32_t input_width = Tensor::dim(input, 2); - - const int32_t output_height = kernels::computeOutputSize( - luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); - const int32_t output_width = kernels::computeOutputSize( - luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); - - const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, - options->filter_height(), output_height); - const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, - options->filter_width(), output_width); - - tflite::PoolParams params{}; - params.padding_values.height = padding_height; - params.padding_values.width = padding_width; - params.stride_height = options->stride_h(); - params.stride_width = options->stride_w(); - params.filter_height = options->filter_height(); - params.filter_width = options->filter_width(); - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - const auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); - - tflite::reference_ops::MaxPool( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); -} - -void evalSInt16(const circle::Tensor *input, const circle::Tensor *output, - const circle::Pool2DOptions *options, BaseRuntimeGraph *runtime_graph) -{ - int32_t activation_min{}; - int32_t activation_max{}; - kernels::calculateActivationRangeQuantized(luci_actfunc(options->fused_activation_function()), - output, &activation_min, &activation_max); - - // Compute padding - const int32_t input_height = Tensor::dim(input, 1); - const int32_t input_width = Tensor::dim(input, 2); - - const int32_t output_height = kernels::computeOutputSize( - luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); - const int32_t output_width = kernels::computeOutputSize( - luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); - - const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, - options->filter_height(), output_height); - const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, - options->filter_width(), output_width); - - tflite::PoolParams params{}; - params.padding_values.height = padding_height; - params.padding_values.width = padding_width; - params.stride_height = options->stride_h(); - params.stride_width = options->stride_w(); - params.filter_height = options->filter_height(); - params.filter_width = options->filter_width(); - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - const auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); - - tflite::reference_integer_ops::MaxPool( - params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); -} - -#endif // DIS_QUANT - -} // namespace - void configure_kernel_CircleMaxPool2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { @@ -184,8 +50,7 @@ void configure_kernel_CircleMaxPool2D(const circle::Operator *cur_op, #endif // DIS_QUANT } -void execute_kernel_CircleMaxPool2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, - bool) +void execute_kernel_CircleMaxPool2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); const auto output_index = cur_op->outputs()->operator[](0); @@ -198,19 +63,55 @@ void execute_kernel_CircleMaxPool2D(const circle::Operator *cur_op, BaseRuntimeG const auto *options = cur_op->builtin_options_as_Pool2DOptions(); + const int32_t input_height = Tensor::dim(input, 1); + const int32_t input_width = Tensor::dim(input, 2); + + const int32_t output_height = kernels::computeOutputSize( + luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); + const int32_t output_width = kernels::computeOutputSize( + luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); + + const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, + options->filter_height(), output_height); + const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, + options->filter_width(), output_width); + + const auto *input_data = runtime_graph->getDataByTensor(input); + auto *output_data = runtime_graph->getDataByTensor(output); + + float activation_min{}; + float activation_max{}; + kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), + &activation_min, &activation_max); + luci_interpreter_pal::PoolParams params{}; + params.padding_values.height = padding_height; + params.padding_values.width = padding_width; + params.stride_height = options->stride_h(); + params.stride_width = options->stride_w(); + params.filter_height = options->filter_height(); + params.filter_width = options->filter_width(); + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + switch (Tensor::element_type(input)) { #ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(input, output, options, runtime_graph); + luci_interpreter_pal::MaxPool( + params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; #endif // DIS_FLOAT #ifndef DIS_QUANT case DataType::U8: - evalQuantized(input, output, options, runtime_graph); + luci_interpreter_pal::MaxPool( + params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; case DataType::S16: - evalSInt16(input, output, options, runtime_graph); + luci_interpreter_pal::MaxPool( + params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; #endif // DIS_QUANT default: diff --git a/onert-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp b/onert-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp index 9e3cbac..d7af7d7 100644 --- a/onert-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/MaxPool2D.test.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. 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. @@ -14,16 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// TODO enable it -#if 0 -#include "kernels/MaxPool2D.h" + #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/maxpool2d/FloatMaxPool2DKernel.h" +#include "luci_interpreter/test_models/maxpool2d/NegMaxPool2DKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -31,112 +30,88 @@ using namespace testing; class MaxPool2DTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(MaxPool2DTest, Float) +template +std::vector checkMaxPool2DKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 3, 5, 1}; - std::vector input_data{ - 1, -1, 0, -2, 2, // - -7, -6, -5, -4, -3, // - 5, 4, 3, 6, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - MaxPool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 1, 2, // - 5, 6, // - }; - std::initializer_list ref_output_shape{1, 2, 2, 1}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(MaxPool2DTest, Float_P) +{ + test_kernel::TestDataFloatMaxPool2D test_data_kernel; + std::vector output_data_vector = checkMaxPool2DKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.01f)); } -TEST_F(MaxPool2DTest, Uint8) +TEST_F(MaxPool2DTest, InputOutputTypeMismatch_NEG) { - std::pair quant_param = quantizationParams(-15.9375, 15.9375); - std::vector input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // - }; - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - params.activation = Activation::RELU6; - - MaxPool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0.0, 6.0}; - std::initializer_list ref_output_shape{1, 1, 2, 1}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::NegTestDataInputOutputTypeMismatchMaxPool2DKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(MaxPool2DTest, SInt16) +TEST_F(MaxPool2DTest, Invalid_input_shape_NEG) { - Shape input_shape{1, 3, 5, 1}; - std::vector ref_output_shape{1, 2, 2, 1}; - std::vector input_data{ - 1, -1, 0, -2, 2, // - -7, -6, -5, -4, -3, // - 5, 4, 3, 6, 7, // - }; - std::vector ref_output_data{ - 1, 2, // - 5, 6, // - }; - - Tensor input_tensor = - makeInputTensor(input_shape, 0.2, 0, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.filter_height = 2; - params.filter_width = 3; - params.stride_height = 1; - params.stride_width = 2; - params.activation = Activation::RELU6; - - MaxPool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); + test_kernel::NegTestDataInvalidInputShapeMaxPool2DKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } +TEST_F(MaxPool2DTest, No_quant_params_NEG) +{ + test_kernel::NegTestDataNoQuantParamsMaxPool2DKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +// TODO: add S16 test + } // namespace -} // namespace kernels } // namespace luci_interpreter -#ednif diff --git a/onert-micro/luci-interpreter/src/kernels/Maximum.h b/onert-micro/luci-interpreter/src/kernels/Maximum.h index 275d5d3..3c99e69 100644 --- a/onert-micro/luci-interpreter/src/kernels/Maximum.h +++ b/onert-micro/luci-interpreter/src/kernels/Maximum.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Mean.h b/onert-micro/luci-interpreter/src/kernels/Mean.h index 9739f54..ed07ae5 100644 --- a/onert-micro/luci-interpreter/src/kernels/Mean.h +++ b/onert-micro/luci-interpreter/src/kernels/Mean.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Minimum.h b/onert-micro/luci-interpreter/src/kernels/Minimum.h index ad1c8fc..5ff4035 100644 --- a/onert-micro/luci-interpreter/src/kernels/Minimum.h +++ b/onert-micro/luci-interpreter/src/kernels/Minimum.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/MirrorPad.h b/onert-micro/luci-interpreter/src/kernels/MirrorPad.h index 5919e3d..d3e6e85 100644 --- a/onert-micro/luci-interpreter/src/kernels/MirrorPad.h +++ b/onert-micro/luci-interpreter/src/kernels/MirrorPad.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp b/onert-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp index f6f938c..740d8cb 100644 --- a/onert-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/MirrorPad.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Mul.cpp b/onert-micro/luci-interpreter/src/kernels/Mul.cpp index bac9fe3..75f9b90 100644 --- a/onert-micro/luci-interpreter/src/kernels/Mul.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Mul.cpp @@ -15,134 +15,142 @@ * limitations under the License. */ -#include "kernels/Mul.h" +#include "Builders.h" +#include "kernels/Utils.h" #include "kernels/BinaryOpCommon.h" -#include "kernels/Utils.h" #include "PALMul.h" -#include - namespace luci_interpreter { -namespace kernels -{ - -Mul::Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams ¶ms) - : KernelWithParams({input1, input2}, {output}, params) -{ -} -void Mul::configure() +void configure_kernel_CircleMul(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == input1()->element_type()); - if (input1()->element_type() == DataType::S16) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); +#ifndef DIS_QUANT + if (Tensor::element_type(kernel.input1()) == DataType::S16) { - LUCI_INTERPRETER_CHECK(input1()->zero_points().size() == 1 && - input2()->zero_points().size() == 1) - LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 && - output()->zero_point() == 0); + LUCI_INTERPRETER_CHECK(Tensor::zero_points(kernel.input1()).size() == 1 && + Tensor::zero_points(kernel.input2()).size() == 1); + LUCI_INTERPRETER_CHECK(Tensor::zero_point(kernel.input1()) == 0 && + Tensor::zero_point(kernel.input2()) == 0 && + Tensor::zero_point(kernel.output()) == 0); } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +#endif // DIS_QUANT } -void Mul::execute() const +void execute_kernel_CircleMul(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (input1()->element_type()) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + const auto *options = cur_op->builtin_options_as_MulOptions(); + + luci_interpreter::RuntimeShape input_shape1 = + kernels::getTensorRuntimeShape(kernel.input1(), runtime_graph); + luci_interpreter::RuntimeShape input_shape2 = + kernels::getTensorRuntimeShape(kernel.input2(), runtime_graph); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input1())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); - break; + { + auto tiso_func = luci_interpreter_pal::Mul; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastMul4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +#endif // DIS_FLOAT case DataType::S64: - evalInteger(); - break; + { + auto tiso_func = luci_interpreter_pal::Mul; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastMul4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; case DataType::S32: - evalInteger(); - break; - case DataType::S16: - evalQuantizedS16(); - break; + { + auto tiso_func = luci_interpreter_pal::Mul; + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastMul4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +#if 0 +#ifndef DIS_QUANT + // TODO: check quantize Mul + case DataType::U8: + { + auto tiso_func = [](const luci_interpreter_pal::ArithmeticParams ¶ms, + const luci_interpreter::RuntimeShape &input1_shape, const uint8_t *input1_data, + const luci_interpreter::RuntimeShape &input2_shape, const uint8_t *input2_data, + const luci_interpreter::RuntimeShape &output_shape, uint8_t *output_data) { + luci_interpreter_pal::Mul(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); + }; + auto broadcast_tiso_func = + [](const luci_interpreter_pal::ArithmeticParams ¶ms, const luci_interpreter::RuntimeShape &input1_shape, + const uint8_t *input1_data, const luci_interpreter::RuntimeShape &input2_shape, + const uint8_t *input2_data, const luci_interpreter::RuntimeShape &output_shape, + uint8_t *output_data) { + luci_interpreter_pal::BroadcastMul4DSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); + }; + if (is_inplace) + { + kernels::evalTISOInplaceQuantizedKernel(tiso_func, broadcast_tiso_func, &kernel, + options); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOQuantizedKernel(tiso_func, broadcast_tiso_func, &kernel, + &kernel_data, options); + } + } + break; +#endif // DIS_QUANT +#endif // 0 default: assert(false && "Unsupported type."); } } -void Mul::evalFloat() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - luci_interpreter_pal::BroadcastMul4DSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - luci_interpreter_pal::Mul(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -template void Mul::evalInteger() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - luci_interpreter_pal::BroadcastMul4DSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - luci_interpreter_pal::Mul(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -void Mul::evalQuantizedS16() const -{ - const auto input1_scale = static_cast(input1()->scale()); - const auto input2_scale = static_cast(input2()->scale()); - const auto output_scale = static_cast(output()->scale()); - - const double real_multiplier = input1_scale * input2_scale / output_scale; - - int32_t output_multiplier; - int output_shift; - quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); - - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - auto fn = [output_multiplier, output_shift, activation_min, activation_max](int16_t input1_val, - int16_t input2_val) { - int32_t output = static_cast(input1_val) * static_cast(input2_val); - output = tflite::MultiplyByQuantizedMultiplier(output, output_multiplier, output_shift); - output = std::max(output, activation_min); - output = std::min(output, activation_max); - return static_cast(output); - }; - - BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output()), fn); -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Mul.h b/onert-micro/luci-interpreter/src/kernels/Mul.h deleted file mode 100644 index 6945abd..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Mul.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_MUL_H -#define LUCI_INTERPRETER_KERNELS_MUL_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -#include -#include - -namespace luci_interpreter -{ -namespace kernels -{ - -class Mul : public KernelWithParams -{ -public: - Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams ¶ms); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantizedS16() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_MUL_H diff --git a/onert-micro/luci-interpreter/src/kernels/Mul.test.cpp b/onert-micro/luci-interpreter/src/kernels/Mul.test.cpp index fc0e606..391c783 100644 --- a/onert-micro/luci-interpreter/src/kernels/Mul.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Mul.test.cpp @@ -15,14 +15,15 @@ * limitations under the License. */ -#include "kernels/Mul.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/mul/FloatMulKernel.h" +#include "luci_interpreter/test_models/mul/IntMulKernel.h" +#include "luci_interpreter/test_models/mul/NegMulKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,263 +31,125 @@ using namespace testing; class MulTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(MulTest, Float) +template std::vector checkMulKernel(test_kernel::TestDataBase *test_data_base) { - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> test_outputs = { - {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, - 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, - 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, - 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, - 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, - {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; - std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - for (size_t i = 0; i < test_shapes.size(); ++i) - { - Tensor input1_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - MulParams params{}; - params.activation = Activation::RELU; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - // Re-run with exchanged inputs. - for (size_t i = 0; i < test_shapes.size(); ++i) + + // set right input data { - Tensor input1_tensor = - makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - MulParams params{}; - params.activation = Activation::RELU; + runtime_module.execute(); - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; - } + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template void checkInteger(luci_interpreter::IMemoryManager *memory_manager) +TEST_F(MulTest, Float_P) { - using dtype = typename loco::DataTypeImpl::Type; - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - - dtype max_value = std::numeric_limits::max(); - dtype res_max = max_value - max_value % 10; - - std::vector> test_outputs = { - {8, 0, 20, 0, 4, 30, // - 16, 0, 40, 3, 8, 0, // - 0, 0, 0, 6, 0, 0, // - 4, 0, 10, 9, 2, 0, // - 40, 0, 100, 0, 20, 150, // - 28, 0, 70, 0, 14, res_max}, - {8, 0, 40, 3, 0, 0, 4, 0, 100, 0, 14, res_max}, - {8, 12, 0, 0, 20, 30, 16, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 2, 0, 10, 0, 0, 0, 20, 30, 100, 150, 0, 0, 14, max_value / 10 * 2, - 70, res_max}, - {8, 12, 0, 0, 0, 0, 0, 9, 20, 30, 70, res_max}}; - std::vector input1_data{2, 3, 4, -1, -3, -2, 1, -3, 10, 15, 7, max_value / 10}; - std::vector input2_data{4, 0, 10, -3, 2, 10}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestDataFloatMul test_data_kernel(is_with_broadcast); + std::vector output_data_vector = checkMulKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } - // Re-run with exchanged inputs. - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor input2_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestDataFloatMul test_data_kernel(is_with_broadcast); + std::vector output_data_vector = checkMulKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } } -TEST_F(MulTest, SInt64) +TEST_F(MulTest, INT_P) { - checkInteger(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(MulTest, SInt32) -{ - checkInteger(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(MulTest, SInt16) -{ - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> ref_output_shapes{ - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; - - std::vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - std::vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - std::vector> ref_outputs = { - {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, - 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, - 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, - 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, - 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, - {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, 3.0 / 32767, 0, input1_data, - _memory_manager.get()); - Tensor input2_tensor = makeInputTensor(test_shapes[i], 1.0 / 32767, 0, - input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); - const float tolerance = output_tensor.scale() * 2; - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), - ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + const bool is_with_broadcast = false; + test_kernel::TestDataIntMul test_data_kernel(is_with_broadcast); + const auto output_data_vector = checkMulKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } - // Re-run with exchanged inputs and different scales. - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = makeInputTensor(test_shapes[i], 2.0 / 32767, 0, - input2_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor(base_shape, 4.0 / 32767, 0, input1_data, - _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 3.0 / 32767, 0); - const float tolerance = output_tensor.scale() * 2; - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), - ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestDataIntMul test_data_kernel(is_with_broadcast); + const auto output_data_vector = checkMulKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } } -TEST_F(MulTest, Input_Output_Type_NEG) +TEST_F(MulTest, Wrong_Input1_Type_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInput1WrongTypeMul test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(MulTest, Invalid_Output_Type_NEG) +TEST_F(MulTest, Wrong_Input2_Type_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInput2WrongTypeMul test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(MulTest, Invalid_Input_Type_NEG) +TEST_F(MulTest, Wrong_Ouput_Type_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U64); - - MulParams params{}; - params.activation = Activation::RELU; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataInt16TypeMul test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(MulTest, Invalid_Quantization_NEG) -{ - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16); - - MulParams params{}; - params.activation = Activation::NONE; - - Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add tests for U8 and S16 +// TODO: add tests for inplace optimizations for all types } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Neg.cpp b/onert-micro/luci-interpreter/src/kernels/Neg.cpp index eae787a..3f08df3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Neg.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Neg.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,43 +14,66 @@ * limitations under the License. */ -#include "kernels/Neg.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "kernels/BinaryOpCommon.h" + #include "PALNeg.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleNeg(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); -Neg::Neg(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + assert(input_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == Tensor::element_type(output)); + + assert(Tensor::num_dims(input) == 4); -void Neg::configure() -{ - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); + // output-> resize(input->shape()); } -void Neg::execute() const +void execute_kernel_CircleNeg(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (input()->element_type()) + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + const uint8_t *input_data = runtime_graph->getDataByTensor(input); + uint8_t *output_data = runtime_graph->getDataByTensor(output); + + assert(input_data != nullptr); + assert(output_data != nullptr); + + switch (Tensor::element_type(input)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); + + luci_interpreter_pal::Negate( + kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); + break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -void Neg::evalFloat() const -{ - luci_interpreter_pal::Negate(getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Neg.h b/onert-micro/luci-interpreter/src/kernels/Neg.h deleted file mode 100644 index f5f565d..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Neg.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_NEG_H -#define LUCI_INTERPRETER_KERNELS_NEG_H - -#include "core/Kernel.h" -#include - -namespace luci_interpreter -{ -namespace kernels -{ - -class Neg : public Kernel -{ -public: - Neg(const Tensor *input, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_NEG_H diff --git a/onert-micro/luci-interpreter/src/kernels/Neg.test.cpp b/onert-micro/luci-interpreter/src/kernels/Neg.test.cpp index 8b2bc1a..0cb0a27 100644 --- a/onert-micro/luci-interpreter/src/kernels/Neg.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Neg.test.cpp @@ -15,57 +15,86 @@ * limitations under the License. */ -#include "kernels/Neg.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/neg/FloatNegKernel.h" +#include "luci_interpreter/test_models/neg/NegNegKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template -void Check(std::initializer_list input_shape, std::initializer_list output_shape, - std::initializer_list input_data, std::initializer_list output_data) +class NegTest : public ::testing::Test +{ + // Do nothing +}; + +template std::vector checkNegKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(element_type); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Neg kernel(&input_tensor, &output_tensor); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST(NegTest, FloatSimple) +TEST_F(NegTest, Float_P) { - Check(/*input_shape=*/{2, 3}, - /*output_shape=*/{2, 3}, - /*input_data=*/ - { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 - }, - /*output_data=*/ - { - 0.0f, -1.0f, -3.0f, // Row 1 - -1.0f, 1.0f, 2.0f, // Row 2 - }); - - SUCCEED(); + test_kernel::TestDataFloatNeg test_data_kernel; + std::vector output_data_vector = checkNegKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} + +TEST_F(NegTest, Input_output_type_mismatch_NEG) +{ + test_kernel::NegTestDataInputOutputTypeMismatchNegKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(NegTest, Invalid_input_shape_NEG) +{ + test_kernel::NegTestDataInvalidInputShapeNegKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/NotEqual.cpp b/onert-micro/luci-interpreter/src/kernels/NotEqual.cpp index 304939e..92f646f 100644 --- a/onert-micro/luci-interpreter/src/kernels/NotEqual.cpp +++ b/onert-micro/luci-interpreter/src/kernels/NotEqual.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,128 +14,76 @@ * limitations under the License. */ -#include "kernels/NotEqual.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "TISOKernel.h" -#include +#include "PALComparisons.h" namespace luci_interpreter { -namespace kernels +namespace { +// TODO: reduce code duplication with less +template +void evalGeneric(const circle::Tensor *x, const circle::Tensor *y, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) +{ + auto x_data = kernels::getTensorData(runtime_graph->getDataByTensor(x)); + if (x_data == nullptr) + x_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(x)); + + assert(x_data != nullptr); + + auto y_data = kernels::getTensorData(runtime_graph->getDataByTensor(y)); + if (y_data == nullptr) + y_data = kernels::getTensorData(runtime_graph->getConstDataByTensor(y)); + + assert(y_data != nullptr); + + auto output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); -NotEqual::NotEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {} + luci_interpreter_pal::ComparisonParams op_params; + op_params.is_broadcast = Tensor::num_elements(x) != Tensor::num_elements(y); -void NotEqual::configure() + const int64_t flat_size = kernels::getTensorShape(x).flatSize(); + luci_interpreter_pal::ComparisonNoScaling(flat_size, x_data, y_data, output_data, + luci_interpreter_pal::NotEqualFn); +} + +} // namespace + +void configure_kernel_CircleNotEqual(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type()); - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL); + kernels::TISOKernel kernel(cur_op, runtime_graph); - if (x()->element_type() == DataType::U8) - { - quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift); - quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::BOOL); } -void NotEqual::execute() const +void execute_kernel_CircleNotEqual(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (x()->element_type()) + kernels::TISOKernel kernel(cur_op, runtime_graph); + + switch (Tensor::element_type(kernel.input1())) { - case DataType::FLOAT32: - evalFloat(); - break; case DataType::S64: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; case DataType::S32: - evalInteger(); + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; - case DataType::U8: - evalQuantized(); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + evalGeneric(kernel.input1(), kernel.input2(), kernel.output(), runtime_graph); break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -void NotEqual::evalFloat() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowNotEqual(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::NotEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()), - y_data, getTensorShape(output()), output_data); - } -} - -template void NotEqual::evalInteger() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowNotEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } - else - { - tflite::reference_ops::NotEqualNoScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } -} - -void NotEqual::evalQuantized() const -{ - const auto x_data = getTensorData(x()); - const auto y_data = getTensorData(y()); - auto output_data = getTensorData(output()); - - tflite::ComparisonParams op_params; - op_params.left_shift = 8; - op_params.input1_offset = -x()->zero_point(); // Note the '-' - op_params.input1_shift = _x_shift; - op_params.input1_multiplier = _x_multiplier; - op_params.input2_offset = -y()->zero_point(); // Note the '-' - op_params.input2_shift = _y_shift; - op_params.input2_multiplier = _y_multiplier; - op_params.is_broadcast = x()->shape() != y()->shape(); - - if (op_params.is_broadcast) - { - tflite::reference_ops::Broadcast4DSlowNotEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - output_data); - } - else - { - tflite::reference_ops::NotEqualWithScaling(op_params, getTensorShape(x()), x_data, - getTensorShape(y()), y_data, - getTensorShape(output()), output_data); - } -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/NotEqual.h b/onert-micro/luci-interpreter/src/kernels/NotEqual.h deleted file mode 100644 index baa22a6..0000000 --- a/onert-micro/luci-interpreter/src/kernels/NotEqual.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H -#define LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class NotEqual : public Kernel -{ -public: - NotEqual(const Tensor *x, const Tensor *y, Tensor *output); - - const Tensor *x() const { return _inputs[0]; } - const Tensor *y() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; - -private: - int32_t _x_multiplier = 0; - int _x_shift = 0; - int32_t _y_multiplier = 0; - int _y_shift = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H diff --git a/onert-micro/luci-interpreter/src/kernels/NotEqual.test.cpp b/onert-micro/luci-interpreter/src/kernels/NotEqual.test.cpp index 45bf402..520a02e 100644 --- a/onert-micro/luci-interpreter/src/kernels/NotEqual.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/NotEqual.test.cpp @@ -15,14 +15,13 @@ * limitations under the License. */ -#include "kernels/NotEqual.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/notequal/FloatNotEqualKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,277 +29,61 @@ using namespace testing; class NotEqualTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(NotEqualTest, FloatSimple) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, false, true, // Row 1 - true, false, true, // Row 2 - }; - - Tensor x_tensor = makeInputTensor({2, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({2, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(NotEqualTest, FloatBroardcast) -{ - std::vector x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - 0.9, 0.7, 0.5, // Row 4 - }; - - std::vector y_data{ - 0.9, 0.7, 0.5, // Row 1 - }; - - std::vector ref_output_data{ - true, false, true, // Row 1 - true, true, true, // Row 2 - true, true, true, // Row 3 - false, false, false, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1, 3}, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} - -template -void checkIntegerSimple(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{min_value, 2, max_value}; - - std::vector y_data{min_value, -2, max_value}; - - std::vector ref_output_data{false, true, false}; - - Tensor x_tensor = makeInputTensor({3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3})); -} - -template -void checkIntegerBroadcast(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - dtype min_value = std::numeric_limits::min(); - dtype max_value = std::numeric_limits::max(); - std::vector x_data{ - min_value, 2, 3, // Row 1 - 4, 5, max_value, // Row 2 - -1, -2, -3, // Row 3 - min_value, -2, max_value, // Row 4 - }; - - std::vector y_data{ - min_value, -2, max_value, // Row 1 - }; - - std::vector ref_output_data{ - false, true, true, // Row 1 - true, true, false, // Row 2 - true, false, true, // Row 3 - false, false, false, // Row 4 - }; - - Tensor x_tensor = makeInputTensor({4, 3}, x_data, memory_manager); - Tensor y_tensor = makeInputTensor({3}, y_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3})); -} - -TEST_F(NotEqualTest, Int32) -{ - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); -} - -TEST_F(NotEqualTest, Int64) -{ - checkIntegerSimple(_memory_manager.get()); - checkIntegerBroadcast(_memory_manager.get()); - SUCCEED(); -} - -// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. -const float F_MIN = -128.0 / 128.0; -const float F_MAX = 127.0 / 128.0; - -TEST_F(NotEqualTest, Uint8Quantized) -{ - std::vector x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 - }; - - std::vector y_data{ - 0.9, 0.5, 0.55, 0.5, // Row 1 - -1, 0, 0.05, 1, // Row 2 - }; - - std::vector ref_output_data{ - true, false, true, true, // Row 1 - true, false, false, true, // Row 2 - }; - - std::pair x_quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data, _memory_manager.get()); - - std::pair y_quant_param = quantizationParams(F_MIN * 2, F_MAX * 2); - Tensor y_tensor = makeInputTensor( - {1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} - -TEST_F(NotEqualTest, Uint8QuantizedBroadcast) +template +std::vector checkNotEqualKernel(test_kernel::TestDataBase *test_data_base) { - std::vector x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - -1, 0.05, 0, 1, // Row 4 - }; - - std::vector y_data{ - -1, 0.05, 0, 1, // Row 1 - }; - - std::vector ref_output_data{ - true, true, true, true, // Row 1 - true, true, false, true, // Row 2 - true, true, true, true, // Row 3 - false, false, false, false, // Row 4 - }; - - std::pair quant_param = quantizationParams(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor( - {1, 4, 4, 1}, quant_param.first, quant_param.second, x_data, _memory_manager.get()); - Tensor y_tensor = makeInputTensor( - {1, 1, 4, 1}, quant_param.first, quant_param.second, y_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(ref_output_data)); -} + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); -TEST_F(NotEqualTest, Input_Type_Mismatch_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -TEST_F(NotEqualTest, Input_Output_Type_NEG) -{ - Tensor x_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + // set right input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} + runtime_module.execute(); -TEST_F(NotEqualTest, Float_Broadcast_NEG) -{ - Tensor x_tensor = makeInputTensor({2}, {1.f, 2.f}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1.f, 2.f, 3.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(NotEqualTest, Int32_Broadcast_NEG) +TEST_F(NotEqualTest, FloatNoBroadcast_P) { - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatNotEqual test_data_kernel(is_with_broadcast, false); + std::vector output_data_vector = checkNotEqualKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(NotEqualTest, Int64_Broadcast_NEG) +TEST_F(NotEqualTest, FloatNoBroadcast_NEG) { - Tensor x_tensor = makeInputTensor({2}, {1, 2}, _memory_manager.get()); - Tensor y_tensor = makeInputTensor({3}, {1, 2, 3}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::BOOL); - - NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); - ASSERT_ANY_THROW(kernel.configure()); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatNotEqual test_data_kernel(is_with_broadcast, true); + EXPECT_DEATH(checkNotEqualKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/OneHot.h b/onert-micro/luci-interpreter/src/kernels/OneHot.h index f1afc28..572f857 100644 --- a/onert-micro/luci-interpreter/src/kernels/OneHot.h +++ b/onert-micro/luci-interpreter/src/kernels/OneHot.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/OneHot.test.cpp b/onert-micro/luci-interpreter/src/kernels/OneHot.test.cpp index bc59d22..45b6968 100644 --- a/onert-micro/luci-interpreter/src/kernels/OneHot.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/OneHot.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/PRelu.cpp b/onert-micro/luci-interpreter/src/kernels/PRelu.cpp index c3c79be..3d64215 100644 --- a/onert-micro/luci-interpreter/src/kernels/PRelu.cpp +++ b/onert-micro/luci-interpreter/src/kernels/PRelu.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/PRelu.h b/onert-micro/luci-interpreter/src/kernels/PRelu.h index b62cc2a..f7735d4 100644 --- a/onert-micro/luci-interpreter/src/kernels/PRelu.h +++ b/onert-micro/luci-interpreter/src/kernels/PRelu.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Pack.cpp b/onert-micro/luci-interpreter/src/kernels/Pack.cpp index 62228f1..7277f22 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pack.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pack.cpp @@ -15,126 +15,114 @@ * limitations under the License. */ -#include "kernels/Pack.h" -#include "kernels/Utils.h" +#include "Builders.h" +#include "Utils.h" -#include +#include namespace luci_interpreter { -namespace kernels +namespace { -Pack::Pack(std::vector inputs, Tensor *output, const PackParams ¶ms) - : KernelWithParams(std::move(inputs), {output}, params) +template +void packImpl(const circle::Tensor *input0, const circle::Tensor *output, + const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, + uint8_t *output_data_raw) { -} + const auto *options = cur_op->builtin_options_as_PackOptions(); + + const int values_count = options->values_count(); + int axis = options->axis(); + const int dimensions = Tensor::num_dims(output); + + const auto input_dims = wrap(input0->shape()); + const auto output_dims = wrap(output->shape()); -void Pack::configure() -{ - LUCI_INTERPRETER_CHECK(_inputs.size() == static_cast(params().values_count)); - const Tensor *t0 = _inputs[0]; - const int dimension_size = t0->shape().num_dims() + 1; - int axis = params().axis; if (axis < 0) { - axis += dimension_size; + axis += dimensions; } - LUCI_INTERPRETER_CHECK(axis >= 0 && axis <= t0->shape().num_dims()); - if (t0->element_type() != DataType::S32 && t0->element_type() != DataType::FLOAT32 && - t0->element_type() != DataType::U8 && t0->element_type() != DataType::S8 && - t0->element_type() != DataType::S16 && t0->element_type() != DataType::S64) - { - assert(false && "Unsupported type."); - } + int outer_size = 1; + for (int i = 0; i < axis; ++i) + outer_size *= output_dims[i]; - for (uint32_t i = 1; i < _inputs.size(); ++i) - { - const Tensor *tensor = _inputs[i]; - LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type()); - LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims()); - for (int d = 0; d < t0->shape().num_dims(); ++d) - { - LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d)); - } - } + int copy_size = 1; + for (int i = axis + 1; i < dimensions; ++i) + copy_size *= output_dims[i]; - Shape output_shape(dimension_size); - int i = 0; - for (int index = 0; index < dimension_size; ++index) - { - if (index == axis) - { - output_shape.dim(index) = params().values_count; - } - else - { - output_shape.dim(index) = t0->shape().dim(i++); - } - } + int input_size = 1; + for (int i = 0; i < input_dims.size(); ++i) + input_size *= input_dims[i]; + + assert(input_size == copy_size * outer_size); + + T *output_data = kernels::getTensorData(output_data_raw); + assert(output_data != nullptr); - if (t0->element_type() == DataType::U8 || t0->element_type() == DataType::S8 || - t0->element_type() == DataType::S16) + for (int i = 0; i < values_count; ++i) { - LUCI_INTERPRETER_CHECK(output()->zero_point() == t0->zero_point()); - LUCI_INTERPRETER_CHECK(output()->scale() == t0->scale()); - // Guarantee input/output quantization params match as we do not support - // packing quantized tensors. - for (int i = 0; i < params().values_count; i++) + const auto input_index = cur_op->inputs()->operator[](i); + assert(input_index != -1); + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + + auto input_data = kernels::getTensorData(runtime_graph->getDataByTensor(input)); + assert(input_data != nullptr); + for (int k = 0; k < outer_size; ++k) { - LUCI_INTERPRETER_CHECK(_inputs[i]->zero_point() == t0->zero_point()); - LUCI_INTERPRETER_CHECK(_inputs[i]->scale() == t0->scale()); + const T *input_ptr = input_data + copy_size * k; + int loc = k * values_count * copy_size + i * copy_size; + T *output_ptr = output_data + loc; + for (int j = 0; j < copy_size; ++j) + output_ptr[j] = input_ptr[j]; } } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); } -void Pack::execute() const +} // namespace + +void configure_kernel_CirclePack(const circle::Operator *, BaseRuntimeGraph *) +{ + // Do nothing +} + +void execute_kernel_CirclePack(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (_inputs[0]->element_type()) + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + assert(output_index != -1); + assert(input_index != -1); + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + auto output_data = runtime_graph->getDataByTensor(output); + assert(output_data != nullptr); + + switch (Tensor::element_type(output)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalGeneric(); - break; - case DataType::U8: - evalGeneric(); + packImpl(input, output, cur_op, runtime_graph, output_data); break; +#endif // DIS_FLOAT +#ifndef DIS_QUANT case DataType::S8: - evalGeneric(); + packImpl(input, output, cur_op, runtime_graph, output_data); break; - case DataType::S16: - evalGeneric(); + case DataType::U8: + packImpl(input, output, cur_op, runtime_graph, output_data); break; +#endif // DIS_QUANT case DataType::S32: - evalGeneric(); + packImpl(input, output, cur_op, runtime_graph, output_data); break; case DataType::S64: - evalGeneric(); + packImpl(input, output, cur_op, runtime_graph, output_data); break; default: - assert(false && "Unsupported type."); - } -} - -template void Pack::evalGeneric() const -{ - const Tensor *t0 = _inputs[0]; - const int dimension_size = t0->shape().num_dims() + 1; - int axis = params().axis; - if (axis < 0) - { - axis += dimension_size; + assert(false && "Unsupported types"); } - - VectorOfTensors inputs(_inputs); - tflite::PackParams params{}; - params.axis = axis; - params.inputs_count = _inputs.size(); - tflite::reference_ops::Pack(params, inputs.shapes(), inputs.data(), getTensorShape(output()), - getTensorData(output())); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Pack.h b/onert-micro/luci-interpreter/src/kernels/Pack.h deleted file mode 100644 index 730177b..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Pack.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_PACK_H -#define LUCI_INTERPRETER_KERNELS_PACK_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Pack : public KernelWithParams -{ -public: - Pack(std::vector inputs, Tensor *output, const PackParams ¶ms); - - const Tensor *input(int index) const { return _inputs[index]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - template void evalGeneric() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_PACK_H diff --git a/onert-micro/luci-interpreter/src/kernels/Pack.test.cpp b/onert-micro/luci-interpreter/src/kernels/Pack.test.cpp index cd5cff9..db820b5 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pack.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pack.test.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,150 +14,82 @@ * limitations under the License. */ -#include "kernels/Pack.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/pack/PackKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template -void Check(std::vector> input_shapes, - std::initializer_list output_shape, std::vector> input_datas, - std::initializer_list output_data, int32_t axis) +class PackTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - std::vector inputs(input_datas.size()); - std::vector tmp_inputs; - for (int i = 0; i < input_datas.size(); i++) - { - if (std::is_same::value || std::is_same::value || - std::is_same::value) - { - tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {}, "")); - memory_manager->allocate_memory(tmp_inputs[i]); - tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); - } - else if (std::is_same::value || std::is_same::value) - { - tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f / 255}, {128}}, "")); - memory_manager->allocate_memory(tmp_inputs[i]); - tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); - } - else - { - assert((std::is_same::value) && "unexpected dtype is tested"); - tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f}, {0}}, "")); - memory_manager->allocate_memory(tmp_inputs[i]); - tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); - } - } - for (int i = 0; i < input_datas.size(); i++) - { - inputs[i] = &tmp_inputs[i]; - } + // Do nothing +}; + +template std::vector checkPackKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Tensor output_tensor = makeOutputTensor(element_type); - if (std::is_same::value || std::is_same::value) + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); + + // Set input 1 data { - output_tensor = makeOutputTensor(element_type, 1.0f / 255, 128); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - else if (std::is_same::value) + + // Set input 2 data { - output_tensor = makeOutputTensor(element_type, 1.0f, 0); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); } - PackParams params{}; - params.axis = axis; - params.values_count = input_datas.size(); - Pack kernel(inputs, &output_tensor, params); + runtime_module.execute(); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template class PackTest : public ::testing::Test +TEST_F(PackTest, Float_P) { -}; - -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(PackTest, DataTypes); - -TYPED_TEST(PackTest, ThreeInputs) -{ - Check(/*input_shapes=*/{{2}, {2}, {2}}, - /*output_shape=*/{3, 2}, - /*input_datas=*/ - {{1, 4}, {2, 5}, {3, 6}}, - /*output_data=*/ - {1, 4, 2, 5, 3, 6}, /*axis=*/0); - - SUCCEED(); + test_kernel::TestDataFloatPack test_data_pack_kernel; + std::vector output_data_vector = checkPackKernel(&test_data_pack_kernel); + EXPECT_THAT(output_data_vector, test_data_pack_kernel.get_output_data_by_index(0)); } -TYPED_TEST(PackTest, NegAxis) +TEST_F(PackTest, Int_P) { - Check(/*input_shapes=*/{{2}, {2}, {2}}, - /*output_shape=*/{2, 3}, - /*input_datas=*/ - {{1, 4}, {2, 5}, {3, 6}}, - /*output_data=*/ - {1, 2, 3, 4, 5, 6}, /*axis=*/-1); - - SUCCEED(); + test_kernel::TestDataIntPack test_data_pack_kernel; + std::vector output_data_vector = checkPackKernel(&test_data_pack_kernel); + EXPECT_THAT(output_data_vector, test_data_pack_kernel.get_output_data_by_index(0)); } -TEST(Pack, MismatchingInputValuesCount_NEG) +TEST_F(PackTest, QuantU8_P) { - std::unique_ptr memory_manager = std::make_unique(); - std::vector input1_data{1, 4}; - std::vector input2_data{2, 5}; - std::vector input3_data{3, 6}; - Tensor input1_tensor = makeInputTensor({2}, input1_data, memory_manager.get()); - Tensor input2_tensor = makeInputTensor({2}, input2_data, memory_manager.get()); - Tensor input3_tensor = makeInputTensor({2}, input3_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - PackParams params{}; - { - params.axis = 0; - params.values_count = 2; - - Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); - } + test_kernel::TestDataQuantU8Pack test_data_pack_kernel; + std::vector output_data_vector = checkPackKernel(&test_data_pack_kernel); + EXPECT_THAT(output_data_vector, test_data_pack_kernel.get_output_data_by_index(0)); } -TEST(Pack, InvalidInputAxis_NEG) -{ - std::unique_ptr memory_manager = std::make_unique(); - std::vector input1_data{1, 4}; - std::vector input2_data{2, 5}; - std::vector input3_data{3, 6}; - Tensor input1_tensor = makeInputTensor({2}, input1_data, memory_manager.get()); - Tensor input2_tensor = makeInputTensor({2}, input2_data, memory_manager.get()); - Tensor input3_tensor = makeInputTensor({2}, input3_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - PackParams params{}; - { - params.axis = 2; - params.values_count = 3; - - Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); - } -} +// TODO: add negative tests? } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Pad.cpp b/onert-micro/luci-interpreter/src/kernels/Pad.cpp index 9ae0f82..18af756 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pad.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pad.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,101 +14,19 @@ * limitations under the License. */ -#include "kernels/Pad.h" - -#include "kernels/Utils.h" - -#include - -#include +#include "Builders.h" +#include "PadCommon.h" namespace luci_interpreter { -namespace kernels -{ - -Pad::Pad(const Tensor *input, const Tensor *paddings, Tensor *output) - : Kernel({input, paddings}, {output}) -{ -} - -void Pad::configure() +void configure_kernel_CirclePad(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const Shape &input_shape = input()->shape(); - const int num_dims = input_shape.num_dims(); - - if (num_dims > 4) - assert(false && "Unsupported number of dimensions."); - - assert(output()->element_type() == input()->element_type()); - assert(paddings()->element_type() == DataType::S32); - // Paddings shape should be [N, 2]. - assert(paddings()->shape().num_dims() == 2); - assert(paddings()->shape().dim(0) == num_dims); - assert(paddings()->shape().dim(1) == 2); - - Shape output_shape(num_dims); - const auto *paddings_data = getTensorData(paddings()); - for (int i = 0; i < num_dims; ++i) - { - const int32_t padding_before = paddings_data[i * 2]; - const int32_t padding_after = paddings_data[i * 2 + 1]; - assert(padding_before >= 0 && padding_after >= 0); - output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after; - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); + configure_kernel_CirclePadCommon(cur_op, runtime_graph); } -void Pad::execute() const +void execute_kernel_CirclePad(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const int num_dims = input()->shape().num_dims(); - - tflite::PadParams params{}; - params.left_padding_count = num_dims; - params.right_padding_count = num_dims; - - const auto *paddings_data = getTensorData(paddings()); - for (int i = num_dims - 1; i >= 0; --i) - { - params.left_padding[i] = paddings_data[i * 2]; - params.right_padding[i] = paddings_data[i * 2 + 1]; - } - - switch (input()->element_type()) - { - case DataType::FLOAT32: - { - const float pad_value = 0.0f; - tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), - &pad_value, getTensorShape(output()), - getTensorData(output())); - break; - } - case DataType::U8: - { - assert(output()->zero_point() >= std::numeric_limits::min()); - assert(output()->zero_point() <= std::numeric_limits::max()); - const auto pad_value = static_cast(output()->zero_point()); - tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), - &pad_value, getTensorShape(output()), - getTensorData(output())); - break; - } - case DataType::S8: - { - assert(output()->zero_point() >= std::numeric_limits::min()); - assert(output()->zero_point() <= std::numeric_limits::max()); - const auto pad_value = static_cast(output()->zero_point()); - tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), - &pad_value, getTensorShape(output()), - getTensorData(output())); - break; - } - default: - assert(false && "Unsupported type."); - } + execute_kernel_CirclePadCommon(cur_op, runtime_graph); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Pad.h b/onert-micro/luci-interpreter/src/kernels/Pad.h deleted file mode 100644 index f6a9cfc..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Pad.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_PAD_H -#define LUCI_INTERPRETER_KERNELS_PAD_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Pad : public Kernel -{ -public: - Pad(const Tensor *input, const Tensor *paddings, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *paddings() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_PAD_H diff --git a/onert-micro/luci-interpreter/src/kernels/Pad.test.cpp b/onert-micro/luci-interpreter/src/kernels/Pad.test.cpp index 95a28b6..3c835cb 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pad.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pad.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,96 +14,73 @@ * limitations under the License. */ -#include "kernels/Pad.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/pad/FloatPadKernel.h" +#include "luci_interpreter/test_models/pad/NegPadKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -float GetTolerance(float min, float max) { return (max - min) / 255.0; } +class PadTest : public ::testing::Test +{ + // Do nothing +}; -TEST(Pad, Uint8) +template std::vector checkPadKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::pair quant_param = quantizationParams(-1.0f, 1.0f); - std::vector input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3}; - std::vector paddings_data{0, 0, 0, 2, 1, 3, 0, 0}; - Tensor input_tensor = makeInputTensor( - {1, 2, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); - Tensor paddings_tensor = - makeInputTensor({4, 2}, paddings_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); - - Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0, -0.8, 0.2, 0.9, 0, 0, 0, 0, 0.7, 0.1, -0.3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(ref_output_data, kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1})); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST(Pad, Int8) +TEST_F(PadTest, Float_P) { - std::unique_ptr memory_manager = std::make_unique(); - float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::pair quant_param = quantizationParams(-1.0f, 1.0f); - std::vector input_data{-0.2, 0.4, 0.5, -0.7, -0.1, -0.9, 0.7, 0.1, 0.2}; - std::vector paddings_data{0, 0, 1, 2, 2, 1, 0, 0}; - Tensor input_tensor = makeInputTensor( - {1, 3, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); - Tensor paddings_tensor = - makeInputTensor({4, 2}, paddings_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S8, quant_param.first, quant_param.second); - - Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0, 0, 0, 0, 0, 0, 0, 0, -0.2, 0.4, 0.5, 0, - 0, 0, -0.7, -0.1, -0.9, 0, 0, 0, 0.7, 0.1, 0.2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(ref_output_data, kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 6, 6, 1})); + test_kernel::TestDataFloatPad test_data_kernel; + std::vector output_data_vector = checkPadKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(Pad, Float) +TEST_F(PadTest, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - std::vector input_data{1, 2, 3, 4, 5, 6}; - std::vector paddings_data{1, 0, 0, 2, 0, 3, 0, 0}; - Tensor input_tensor = - makeInputTensor({1, 2, 3, 1}, input_data, memory_manager.get()); - Tensor paddings_tensor = - makeInputTensor({4, 2}, paddings_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pad kernel(&input_tensor, &paddings_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 4, 5, - 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - std::initializer_list ref_output_shape{2, 4, 6, 1}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::NegTestDataInputOutputTypeMismatchPadKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/PadCommon.cpp b/onert-micro/luci-interpreter/src/kernels/PadCommon.cpp new file mode 100644 index 0000000..92cd117 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/PadCommon.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Builders.h" +#include "kernels/Utils.h" + +#include "PadCommon.h" +#include "PALPad.h" + +namespace luci_interpreter +{ +void configure_kernel_CirclePadCommon(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) +{ + const auto num_inputs = cur_op->inputs()->size(); + + const auto input1_index = cur_op->inputs()->operator[](0); + const auto input2_index = cur_op->inputs()->operator[](1); + const auto input3_index = num_inputs == 3 ? cur_op->inputs()->operator[](2) : -1; + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input1_index != -1); + assert(input2_index != -1); + assert(input3_index != -1 or num_inputs == 2); + assert(output_index != -1); + + const auto input1_tensor = runtime_graph->getCircleTensorByIndex(input1_index); + const auto input2_tensor = runtime_graph->getCircleTensorByIndex(input2_index); + const auto input3_tensor = + num_inputs == 3 ? runtime_graph->getCircleTensorByIndex(input3_index) : nullptr; + const auto output_tensor = runtime_graph->getCircleTensorByIndex(output_index); + + assert(input1_tensor != nullptr); + assert(input2_tensor != nullptr); + assert(input3_tensor != nullptr or num_inputs == 2); + assert(output_tensor != nullptr); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(input2_tensor) == DataType::S32); + LUCI_INTERPRETER_CHECK(Tensor::element_type(input1_tensor) == + Tensor::element_type(output_tensor)); + if (input3_tensor != nullptr) + { + LUCI_INTERPRETER_CHECK(Tensor::element_type(input3_tensor) == + Tensor::element_type(input1_tensor)); + // Value is scalar + LUCI_INTERPRETER_CHECK(Tensor::num_elements(input3_tensor) == 1); + } + + // Check shapes + const int32_t *paddings_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(input2_tensor)); + for (int i = 0; i < Tensor::num_dims(output_tensor); i++) + { + int output_dim = Tensor::dim(output_tensor, i); + int expected_dim = + Tensor::dim(input1_tensor, i) + paddings_data[i * 2] + paddings_data[i * 2 + 1]; + LUCI_INTERPRETER_CHECK(output_dim == expected_dim); + } +} + +void execute_kernel_CirclePadCommon(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + const auto num_inputs = cur_op->inputs()->size(); + + const auto input1_index = cur_op->inputs()->operator[](0); + const auto input2_index = cur_op->inputs()->operator[](1); + const auto input3_index = num_inputs == 3 ? cur_op->inputs()->operator[](2) : -1; + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input1_index != -1); + assert(input2_index != -1); + assert(input3_index != -1 or num_inputs == 2); + assert(output_index != -1); + + const auto input1_tensor = runtime_graph->getCircleTensorByIndex(input1_index); + const auto input2_tensor = runtime_graph->getCircleTensorByIndex(input2_index); + const auto input3_tensor = + num_inputs == 3 ? runtime_graph->getCircleTensorByIndex(input3_index) : nullptr; + const auto output_tensor = runtime_graph->getCircleTensorByIndex(output_index); + + assert(input1_tensor != nullptr); + assert(input2_tensor != nullptr); + assert(input3_tensor != nullptr or num_inputs == 2); + assert(output_tensor != nullptr); + + luci_interpreter_pal::PadParams pad_params; + const int num_input_dimensions = Tensor::num_dims(input1_tensor); + pad_params.left_padding_count = num_input_dimensions; + pad_params.right_padding_count = num_input_dimensions; + + const int32_t *paddings_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(input2_tensor)); + for (int idx = num_input_dimensions - 1; idx >= 0; --idx) + { + pad_params.left_padding[idx] = paddings_data[idx * 2]; + pad_params.right_padding[idx] = paddings_data[idx * 2 + 1]; + } + + auto *input1_data = runtime_graph->getDataByTensor(input1_tensor); + if (input1_data == nullptr) + input1_data = runtime_graph->getConstDataByTensor(input1_tensor); + assert(input1_data); + + auto *input2_data = runtime_graph->getConstDataByTensor(input2_tensor); + assert(input2_data); + + auto *output_data = runtime_graph->getDataByTensor(output_tensor); + assert(output_data); + + switch (Tensor::element_type(input1_tensor)) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + float pad_value = + input3_tensor == nullptr + ? 0.f + : *kernels::getTensorData(runtime_graph->getConstDataByTensor(input3_tensor)); + luci_interpreter_pal::Pad(pad_params, kernels::getTensorShape(input1_tensor), + kernels::getTensorData(input1_data), &pad_value, + kernels::getTensorShape(output_tensor), + kernels::getTensorData(output_data)); + } + break; +#endif // DIS_FLOAT + default: + assert(false && "Unsupported type"); + } +} + +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Neg.cpp b/onert-micro/luci-interpreter/src/kernels/PadCommon.h similarity index 56% rename from onert-micro/luci-interpreter/src/loader/nodes/Neg.cpp rename to onert-micro/luci-interpreter/src/kernels/PadCommon.h index a898f41..d8fa4be 100644 --- a/onert-micro/luci-interpreter/src/loader/nodes/Neg.cpp +++ b/onert-micro/luci-interpreter/src/kernels/PadCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,20 @@ * limitations under the License. */ -#include "Builders.h" +#ifndef LUCI_INTERPRETER_KERNELS_PAD_COMMON_H +#define LUCI_INTERPRETER_KERNELS_PAD_COMMON_H -#include "kernels/Neg.h" +#include "Builders.h" +#include "kernels/Utils.h" namespace luci_interpreter { +void configure_kernel_CirclePadCommon(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph); -std::unique_ptr build_kernel_CircleNeg(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} +void execute_kernel_CirclePadCommon(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph); } // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PAD_COMMON_H diff --git a/onert-micro/luci-interpreter/src/kernels/PadV2.cpp b/onert-micro/luci-interpreter/src/kernels/PadV2.cpp index 2b759a8..7e0f61b 100644 --- a/onert-micro/luci-interpreter/src/kernels/PadV2.cpp +++ b/onert-micro/luci-interpreter/src/kernels/PadV2.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * 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. @@ -15,95 +14,19 @@ * limitations under the License. */ -#include "kernels/PadV2.h" - -#include "kernels/Utils.h" - -#include - -#include +#include "Builders.h" +#include "PadCommon.h" namespace luci_interpreter { -namespace kernels -{ - -PadV2::PadV2(const Tensor *input, const Tensor *paddings, const Tensor *constant_values, - Tensor *output) - : Kernel({input, paddings, constant_values}, {output}) -{ -} - -void PadV2::configure() +void configure_kernel_CirclePadV2(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const Shape &input_shape = input()->shape(); - const int num_dims = input_shape.num_dims(); - - if (num_dims > 4) - assert(false && "Unsupported number of dimensions."); - - assert(output()->element_type() == input()->element_type()); - assert(paddings()->element_type() == DataType::S32); - assert(constant_values()->element_type() == output()->element_type()); - // Paddings shape should be [N, 2]. - assert(paddings()->shape().num_dims() == 2); - assert(paddings()->shape().dim(0) == num_dims); - assert(paddings()->shape().dim(1) == 2); - // Constant values elements number should be 1. - assert(constant_values()->shape().num_elements() == 1); - - Shape output_shape(num_dims); - const auto *paddings_data = getTensorData(paddings()); - for (int i = 0; i < num_dims; ++i) - { - const int32_t padding_before = paddings_data[i * 2]; - const int32_t padding_after = paddings_data[i * 2 + 1]; - assert(padding_before >= 0 && padding_after >= 0); - output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after; - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); + configure_kernel_CirclePadCommon(cur_op, runtime_graph); } -void PadV2::execute() const +void execute_kernel_CirclePadV2(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const int num_dims = input()->shape().num_dims(); - - tflite::PadParams params{}; - params.left_padding_count = num_dims; - params.right_padding_count = num_dims; - - const auto *paddings_data = getTensorData(paddings()); - for (int i = num_dims - 1; i >= 0; --i) - { - params.left_padding[i] = paddings_data[i * 2]; - params.right_padding[i] = paddings_data[i * 2 + 1]; - } - - switch (input()->element_type()) - { - case DataType::FLOAT32: - { - const auto pad_value = getTensorData(constant_values())[0]; - tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), - &pad_value, getTensorShape(output()), - getTensorData(output())); - break; - } - case DataType::U8: - { - assert(output()->zero_point() >= std::numeric_limits::min()); - assert(output()->zero_point() <= std::numeric_limits::max()); - const auto pad_value = getTensorData(constant_values())[0]; - tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData(input()), - &pad_value, getTensorShape(output()), - getTensorData(output())); - break; - } - default: - assert(false && "Unsupported type."); - } + execute_kernel_CirclePadCommon(cur_op, runtime_graph); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/PadV2.h b/onert-micro/luci-interpreter/src/kernels/PadV2.h deleted file mode 100644 index 546dba9..0000000 --- a/onert-micro/luci-interpreter/src/kernels/PadV2.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_PAD_V2_H -#define LUCI_INTERPRETER_KERNELS_PAD_V2_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class PadV2 : public Kernel -{ -public: - PadV2(const Tensor *input, const Tensor *paddings, const Tensor *constant_values, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *paddings() const { return _inputs[1]; } - const Tensor *constant_values() const { return _inputs[2]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_PAD_V2_H diff --git a/onert-micro/luci-interpreter/src/kernels/PadV2.test.cpp b/onert-micro/luci-interpreter/src/kernels/PadV2.test.cpp index 9094a22..8be5b02 100644 --- a/onert-micro/luci-interpreter/src/kernels/PadV2.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/PadV2.test.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * 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. @@ -15,77 +14,73 @@ * limitations under the License. */ -#include "kernels/PadV2.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/pad_v2/FloatPadV2Kernel.h" +#include "luci_interpreter/test_models/pad_v2/NegPadV2Kernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -float GetTolerance(float min, float max) { return (max - min) / 255.0; } +class PadV2Test : public ::testing::Test +{ + // Do nothing +}; -TEST(PadV2, Uint8) +template std::vector checkPadV2Kernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - float kQuantizedTolerance = GetTolerance(-1.0, 1.0); - std::pair quant_param = quantizationParams(-1.0f, 1.0f); - std::vector input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3}; - std::vector paddings_data{0, 0, 0, 2, 1, 3, 0, 0}; - std::vector constant_values_data{0.5}; - Tensor input_tensor = makeInputTensor( - {1, 2, 3, 1}, quant_param.first, quant_param.second, input_data, memory_manager.get()); - Tensor paddings_tensor = - makeInputTensor({4, 2}, paddings_data, memory_manager.get()); - Tensor constant_values = makeInputTensor( - {1}, quant_param.first, quant_param.second, constant_values_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); - - PadV2 kernel(&input_tensor, &paddings_tensor, &constant_values, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data = { - 0.5, -0.8, 0.2, 0.9, 0.5, 0.5, 0.5, 0.5, 0.7, 0.1, -0.3, 0.5, 0.5, 0.5, // - 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}; // - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(ref_output_data, kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1})); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(PadV2Test, Float_P) +{ + test_kernel::TestDataFloatPadV2 test_data_kernel; + std::vector output_data_vector = checkPadV2Kernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(PadV2, Float) +TEST_F(PadV2Test, Input_output_type_mismatch_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - std::vector input_data{1, 2, 3, 4, 5, 6}; - std::vector paddings_data{1, 0, 0, 2, 0, 3, 0, 0}; - std::vector constant_values_data{7}; - Tensor input_tensor = - makeInputTensor({1, 2, 3, 1}, input_data, memory_manager.get()); - Tensor paddings_tensor = - makeInputTensor({4, 2}, paddings_data, memory_manager.get()); - Tensor constant_values = - makeInputTensor({1}, constant_values_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - PadV2 kernel(&input_tensor, &paddings_tensor, &constant_values, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 1, 2, 3, 7, 7, 7, 4, 5, - 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; - std::initializer_list ref_output_shape{2, 4, 6, 1}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::NegTestDataInputOutputTypeMismatchPadV2Kernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Pow.cpp b/onert-micro/luci-interpreter/src/kernels/Pow.cpp index 521e80d..6345bf3 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pow.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pow.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Pow.h b/onert-micro/luci-interpreter/src/kernels/Pow.h index 1e7906e..8ff865e 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pow.h +++ b/onert-micro/luci-interpreter/src/kernels/Pow.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Pow.test.cpp b/onert-micro/luci-interpreter/src/kernels/Pow.test.cpp index d71248d..0e85811 100644 --- a/onert-micro/luci-interpreter/src/kernels/Pow.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Pow.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Quantize.cpp b/onert-micro/luci-interpreter/src/kernels/Quantize.cpp index 6c832f7..9f622d0 100644 --- a/onert-micro/luci-interpreter/src/kernels/Quantize.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Quantize.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Quantize.h b/onert-micro/luci-interpreter/src/kernels/Quantize.h index bbb951b..006c536 100644 --- a/onert-micro/luci-interpreter/src/kernels/Quantize.h +++ b/onert-micro/luci-interpreter/src/kernels/Quantize.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/ReduceCommon.cpp b/onert-micro/luci-interpreter/src/kernels/ReduceCommon.cpp new file mode 100644 index 0000000..c93329c --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/ReduceCommon.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Builders.h" +#include "kernels/Utils.h" +#include "TISOKernel.h" + +#include "PALReduceCommon.h" + +#include + +namespace luci_interpreter +{ +namespace +{ + +template +void reduceProdGeneric(kernels::TISOData *tiso_data, const circle::Tensor *input, + const circle::Tensor *axis, const circle::Tensor *output, bool keep_dims) +{ + const int input_rank = Tensor::num_dims(input); + const int num_axis = Tensor::num_elements(axis); + + auto const input_dims = wrap(input->shape()); + const auto output_shape = kernels::getTensorShape(output); + + luci_interpreter_pal::ReduceGeneric( + kernels::getTensorData(tiso_data->input1_data), + reinterpret_cast(input_dims.data()), input_rank, + kernels::getTensorData(tiso_data->output_data), + kernels::getTensorData(tiso_data->input2_data), num_axis, + /*init_value=*/T(1), output_shape.flatSize(), + [](const T current, const T in) -> T { return in * current; }); +} + +} // namespace + +void configure_kernel_CircleReduceCommon(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == DataType::S32 or + Tensor::element_type(kernel.input1()) == DataType::FLOAT32 or + Tensor::element_type(kernel.input1()) == DataType::S64); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32); +} + +void execute_kernel_CircleReduceCommon(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) +{ + kernels::TISOKernel kernel(cur_op, runtime_graph); + kernels::TISOData tiso_data = kernel.readData(); + + const auto *input = kernel.input1(); + const auto *axis = kernel.input2(); + const auto *output = kernel.output(); + + const auto *options = cur_op->builtin_options_as_ReducerOptions(); + + switch (Tensor::element_type(kernel.input1())) + { +#ifndef DIS_FLOAT + case DataType::FLOAT32: + reduceProdGeneric(&tiso_data, input, axis, output, options->keep_dims()); + break; +#endif // DIS_FLOAT + case DataType::S32: + reduceProdGeneric(&tiso_data, input, axis, output, options->keep_dims()); + break; + case DataType::S64: + reduceProdGeneric(&tiso_data, input, axis, output, options->keep_dims()); + break; + default: + assert(false && "Unsupported type"); + } +} + +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ReduceCommon.test.cpp b/onert-micro/luci-interpreter/src/kernels/ReduceCommon.test.cpp new file mode 100644 index 0000000..3357460 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/ReduceCommon.test.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernels/TestUtils.h" +#include "luci_interpreter/test_models/reduce_common/ReduceProdKernel.h" +#include "luci_interpreter/test_models/reduce_common/NegReduceProdKernel.h" + +#include "loader/ModuleLoader.h" + +namespace luci_interpreter +{ +namespace +{ + +using namespace testing; + +class ReduceCommonTest : public ::testing::Test +{ + // Do nothing +}; + +template +std::vector checkReduceCommonKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(ReduceCommonTest, Reduce_Prod_Float_P) +{ + test_kernel::TestDataFloatReduceProd test_data_float_reduce_prod; + std::vector output_data_vector = checkReduceCommonKernel(&test_data_float_reduce_prod); + EXPECT_THAT(output_data_vector, + kernels::testing::FloatArrayNear( + test_data_float_reduce_prod.get_output_data_by_index(0), 0.0001f)); +} + +TEST_F(ReduceCommonTest, Reduce_Prod_Int_P) +{ + test_kernel::TestDataIntReduceProd test_data_int_reduce_prod; + std::vector output_data_vector = checkReduceCommonKernel(&test_data_int_reduce_prod); + EXPECT_THAT(output_data_vector, test_data_int_reduce_prod.get_output_data_by_index(0)); +} + +TEST_F(ReduceCommonTest, Wrong_input_type_NEG) +{ + test_kernel::NegTestDataWrongInputTypeReduceProdKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(ReduceCommonTest, Wrong_axis_type_NEG) +{ + test_kernel::NegTestDataWrongAxisTypeReduceProdKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +} // namespace +} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Relu.cpp b/onert-micro/luci-interpreter/src/kernels/Relu.cpp index 74c2c0f..c38f332 100644 --- a/onert-micro/luci-interpreter/src/kernels/Relu.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Relu.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,100 +14,63 @@ * limitations under the License. */ -#include "kernels/Relu.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include "PALRelu.h" +#include "PALReluCommon.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleRelu(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + kernels::SISOKernel kernel(cur_op, runtime_graph); -Relu::Relu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) == Tensor::num_dims(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input()) == + Tensor::num_elements(kernel.output())); +} -void Relu::configure() +void execute_kernel_CircleRelu(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - 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); - } + kernels::SISOKernel kernel(cur_op, runtime_graph); - if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16) - { - double multiplier = input()->scale() / output()->scale(); - quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); -} + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); -void Relu::execute() const -{ - switch (input()->element_type()) + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); - break; - case DataType::U8: - evalQuantized(); - break; - case DataType::S16: - evalQuantizedS16(); + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::ReLUCommon(flat_size, input_data_float, output_data_float, 0.0f, false); break; + } +#endif // DIS_FLOAT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -} - -void Relu::evalFloat() const -{ - const auto input_data = getTensorData(input()); - const auto input_shape = getTensorShape(input()); - auto output_data = getTensorData(output()); - auto output_shape = getTensorShape(output()); - luci_interpreter_pal::Relu(input_shape, input_data, output_shape, output_data); -} - -void Relu::evalQuantized() const -{ - tflite::ReluParams params; - params.input_offset = input()->zero_point(); - params.output_offset = output()->zero_point(); - params.output_multiplier = _output_multiplier; - params.output_shift = _output_shift; - - params.quantized_activation_min = - std::max(static_cast(std::numeric_limits::min()), params.output_offset); - params.quantized_activation_max = static_cast(std::numeric_limits::max()); - - luci_interpreter_pal::ReluX(params, getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); -} - -void Relu::evalQuantizedS16() const -{ - const auto *input_data = getTensorData(input()); - auto *output_data = getTensorData(output()); - - constexpr int32_t output_min = 0; - constexpr int32_t output_max = std::numeric_limits::max(); - - const int32_t num_elements = input()->shape().num_elements(); - - for (int32_t i = 0; i < num_elements; ++i) - { - const int32_t input_val = input_data[i]; - int32_t output_val = - tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift); - output_val = std::max(output_val, output_min); - output_val = std::min(output_val, output_max); - output_data[i] = static_cast(output_val); - } + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Relu.h b/onert-micro/luci-interpreter/src/kernels/Relu.h deleted file mode 100644 index 7dae445..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Relu.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_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/onert-micro/luci-interpreter/src/kernels/Relu.test.cpp b/onert-micro/luci-interpreter/src/kernels/Relu.test.cpp index bd32e3c..b0cb220 100644 --- a/onert-micro/luci-interpreter/src/kernels/Relu.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Relu.test.cpp @@ -15,154 +15,73 @@ * limitations under the License. */ -#include "kernels/Relu.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/relu/FloatReLUKernel.h" +#include "luci_interpreter/test_models/relu/NegReLUKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -class ReluTest : public ::testing::Test +class ReLUTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(ReluTest, FloatSimple) +template std::vector checkReLUKernel(test_kernel::TestDataBase *test_data_base) { - std::vector input_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 - }; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector ref_output_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, 0.0f, 0.0f, // Row 2 - }; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Tensor input_tensor = - makeInputTensor({2, 3}, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Relu kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); -TEST_F(ReluTest, Uint8Quantized) -{ - std::vector input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // - }; - // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. - const float f_min = (-128.0 / 128.0) * 8; - const float f_max = (127.0 / 128.0) * 8; + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - std::pair quant_param = quantizationParams(f_min, f_max); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + runtime_module.execute(); - Relu kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray({128, 128, 160, 192, 176, 128, 240, 144})); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1})); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(ReluTest, Uint8Requantized) +TEST_F(ReLUTest, Float_P) { - std::vector input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // - }; - - // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. - const float in_min = (-128.0 / 128.0) * 8; - const float in_max = (127.0 / 128.0) * 8; - const float out_min = (0.0 / 256.0) * 8; - const float out_max = (255.0 / 256.0) * 8; - - std::pair quant_input = quantizationParams(in_min, in_max); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_input.first, quant_input.second, input_data, _memory_manager.get()); - - std::pair quant_output = quantizationParams(out_min, out_max); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); - - Relu kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray({0, 0, 64, 128, 96, 0, 224, 32})); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1})); + test_kernel::TestDataFloatReLU test_data_kernel; + std::vector output_data_vector = checkReLUKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(ReluTest, SInt16) +TEST_F(ReLUTest, Input_output_type_mismatch_NEG) { - std::vector input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // - }; - std::vector ref_output_data{ - 0, 0, 2, 4, // - 3, 0, 7, 1, // - }; - - Tensor input_tensor = - makeInputTensor({1, 2, 4, 1}, 0.5, 0, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S16, 0.25, 0); - - Relu kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); -} - -TEST_F(ReluTest, Input_Output_Type_NEG) -{ - Tensor input_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - Relu kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(ReluTest, Invalid_Input_Type_NEG) -{ - Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S64); - - Relu kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataInputOutputTypeMismatchReLUKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Relu6.cpp b/onert-micro/luci-interpreter/src/kernels/Relu6.cpp index 0449708..7186228 100644 --- a/onert-micro/luci-interpreter/src/kernels/Relu6.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Relu6.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,74 +14,63 @@ * limitations under the License. */ -#include "kernels/Relu6.h" +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include "PALRelu6.h" +#include "PALReluCommon.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleRelu6(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + kernels::SISOKernel kernel(cur_op, runtime_graph); -Relu6::Relu6(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) == Tensor::num_dims(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_elements(kernel.input()) == + Tensor::num_elements(kernel.output())); +} -void Relu6::configure() +void execute_kernel_CircleRelu6(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + kernels::SISOKernel kernel(cur_op, runtime_graph); - if (input()->element_type() == DataType::U8) - { - double multiplier = input()->scale() / output()->scale(); - quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift); - } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); -} + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); -void Relu6::execute() const -{ - switch (input()->element_type()) + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); - break; - case DataType::U8: - evalQuantized(); + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::ReLUCommon(flat_size, input_data_float, output_data_float, 0.0f, true); break; + } +#endif // DIS_FLOAT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -} - -void Relu6::evalFloat() const -{ - const auto input_data = getTensorData(input()); - const auto input_shape = getTensorShape(input()); - auto output_data = getTensorData(output()); - auto output_shape = getTensorShape(output()); - - luci_interpreter_pal::Relu6(input_shape, input_data, output_shape, output_data); -} - -void Relu6::evalQuantized() const -{ - tflite::ReluParams params; - params.input_offset = input()->zero_point(); - params.output_offset = output()->zero_point(); - params.output_multiplier = _output_multiplier; - params.output_shift = _output_shift; - - params.quantized_activation_min = - std::max(static_cast(std::numeric_limits::min()), params.output_offset); - params.quantized_activation_max = - std::min(static_cast(std::numeric_limits::max()), - params.output_offset + static_cast(roundf(6.f / output()->scale()))); - luci_interpreter_pal::ReluX(params, getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Relu6.test.cpp b/onert-micro/luci-interpreter/src/kernels/Relu6.test.cpp index af7b3f3..1b784f0 100644 --- a/onert-micro/luci-interpreter/src/kernels/Relu6.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Relu6.test.cpp @@ -15,135 +15,73 @@ * limitations under the License. */ -#include "kernels/Relu6.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/relu6/FloatReLU6Kernel.h" +#include "luci_interpreter/test_models/relu6/NegReLU6Kernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -class Relu6Test : public ::testing::Test +class ReLU6Test : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(Relu6Test, FloatSimple) -{ - std::vector input_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 7.0f, -1.0f, -2.0f, // Row 2 - }; - - std::vector ref_output_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 6.0f, 0.0f, 0.0f, // Row 2 - }; - - Tensor input_tensor = - makeInputTensor({2, 3}, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Relu6 kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray(ref_output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3})); -} - -TEST_F(Relu6Test, Uint8Quantized) +template std::vector checkReLU6Kernel(test_kernel::TestDataBase *test_data_base) { - // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. - const float f_min = (-128.0 / 128.0) * 10; - const float f_max = (127.0 / 128.0) * 10; - const float tolerance = (f_max - f_min) / 255.0; - - std::vector input_data{ - 0, -6, 2, 8, // - -2, 3, 7, 1, // - }; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::pair quant_param = quantizationParams(f_min, f_max); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_param.first, quant_param.second, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Relu6 kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray({128, 128, 154, 205, 128, 166, 205, 141})); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance)); -} - -TEST_F(Relu6Test, Uint8Requantized) -{ - // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. - const float in_min = (-128.0 / 128.0) * 10; - const float in_max = (127.0 / 128.0) * 10; - const float out_min = (0.0 / 256.0) * 0; - const float out_max = (255.0 / 256.0) * 6; - const float tolerance = (in_max - in_min) / 255.0; + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - std::vector input_data{ - 0, -6, 2, 8, // - -2, 3, 7, 1, // - }; + runtime_module.execute(); - std::pair quant_input = quantizationParams(in_min, in_max); - Tensor input_tensor = makeInputTensor( - {1, 2, 4, 1}, quant_input.first, quant_input.second, input_data, _memory_manager.get()); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - std::pair quant_output = quantizationParams(out_min, out_max); - Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); - - Relu6 kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1})); - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray({0, 0, 87, 255, 0, 127, 255, 43})); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(Relu6Test, Input_Output_Type_NEG) +TEST_F(ReLU6Test, Float_P) { - Tensor input_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - Relu6 kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataFloatReLU6 test_data_kernel; + std::vector output_data_vector = checkReLU6Kernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(Relu6Test, Invalid_Input_Type_NEG) +TEST_F(ReLU6Test, Input_output_type_mismatch_NEG) { - Tensor input_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S64); - - Relu6 kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataInputOutputTypeMismatchReLU6Kernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Reshape.cpp b/onert-micro/luci-interpreter/src/kernels/Reshape.cpp index ba47df4..7fe3e56 100644 --- a/onert-micro/luci-interpreter/src/kernels/Reshape.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Reshape.cpp @@ -16,6 +16,7 @@ */ #include "Builders.h" +#include "Utils.h" #include #include @@ -23,35 +24,75 @@ namespace luci_interpreter { -void configure_kernel_CircleReshape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +void configure_kernel_CircleReshape(const circle::Operator *, BaseRuntimeGraph *) { // Do nothing } -void execute_kernel_CircleReshape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, - bool is_inplace) +// TODO: reduce code duplication with ExpandDims +void execute_kernel_CircleReshape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); + const auto shape_index = cur_op->inputs()->operator[](1); const auto output_index = cur_op->outputs()->operator[](0); assert(input_index != -1); + assert(shape_index != -1); assert(output_index != -1); const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto shape = runtime_graph->getCircleTensorByIndex(shape_index); const auto output = runtime_graph->getCircleTensorByIndex(output_index); - + bool is_inplace = runtime_graph->is_inplace_op(cur_op); if (is_inplace) { runtime_graph->makeInplaceOperation(input, output); return; } - const auto input_data = (runtime_graph->getDataByTensor(input)); - auto output_data = (runtime_graph->getDataByTensor(output)); + const auto input_data = runtime_graph->getDataByTensor(input); + auto shape_data = runtime_graph->getConstDataByTensor(shape); + auto output_data = runtime_graph->getDataByTensor(output); assert(input_data != nullptr); assert(output_data != nullptr); +#ifndef DIS_DYN_SHAPES + if (shape_data == nullptr) + { + shape_data = runtime_graph->getDataByTensor(shape); + assert(shape_data != nullptr); + + assert(Tensor::element_type(shape) == DataType::S32); + + const int32_t *shape_data_int = kernels::getTensorData(shape_data); + const auto num_elements = Tensor::num_elements(shape); + + luci_interpreter::RuntimeShape dynamic_shape(num_elements); + int32_t data_size = 1; + for (int i = 0; i < num_elements; ++i) + { + dynamic_shape.setDim(i, shape_data_int[i]); + data_size *= shape_data_int[i]; + } + data_size *= size(Tensor::element_type(output)); + + runtime_graph->addDynamicShapeTensor(output, std::move(dynamic_shape)); + + if (data_size == 0) + { + runtime_graph->resetTensorData(nullptr, output); + return; + } + + auto new_output_data = new uint8_t[data_size]; + output_data = new_output_data; + runtime_graph->resetTensorData(new_output_data, output); + } +#else + assert(shape_data != nullptr); +#endif // DIS_DYN_SHAPES + const size_t element_size = getDataTypeSize(Tensor::element_type(input)); const int32_t num_elements = Tensor::num_elements(input); std::memcpy(output_data, input_data, num_elements * element_size); diff --git a/onert-micro/luci-interpreter/src/kernels/Reshape.test.cpp b/onert-micro/luci-interpreter/src/kernels/Reshape.test.cpp index 7a2a638..ac1acfb 100644 --- a/onert-micro/luci-interpreter/src/kernels/Reshape.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Reshape.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -14,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#if 0 -#include "kernels/Reshape.h" + #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/reshape/ReshapeKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,55 +28,52 @@ using namespace testing; class ReshapeTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -// TODO Test types other than FLOAT32. - -TEST_F(ReshapeTest, Regular) +template +std::vector checkReshapeKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 2, 2, 3}; - std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - Shape shape_shape{2}; - std::vector shape_data{3, 4}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor shape_tensor = - makeInputTensor(shape_shape, shape_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(ReshapeTest, UnknownDimension) +TEST_F(ReshapeTest, MainTest_P) { - Shape input_shape{2, 1, 2, 3}; - std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - Shape shape_shape{3}; - std::vector shape_data{2, -1, 2}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor shape_tensor = - makeInputTensor(shape_shape, shape_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Reshape kernel(&input_tensor, &shape_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + test_kernel::TestDataReshapeKernel test_data_kernel(false); + std::vector output_data_vector = checkReshapeKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(input_data)); +TEST_F(ReshapeTest, MainTest_NEG) +{ + test_kernel::TestDataReshapeKernel test_data_kernel(true); + EXPECT_DEATH(checkReshapeKernel(&test_data_kernel), ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter -#endif diff --git a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp index 8d07ea7..7ce3833 100644 --- a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 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"); @@ -15,61 +15,104 @@ * limitations under the License. */ -#include "kernels/ResizeBilinear.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "kernels/BinaryOpCommon.h" + #include "PALResizeBilinear.h" namespace luci_interpreter { -namespace kernels -{ -ResizeBilinear::ResizeBilinear(const Tensor *input, const Tensor *size, Tensor *output, - const ResizeBilinearParams ¶ms) - : KernelWithParams({input, size}, {output}, params) -{ -} +/* + * ResizeBilinear Kernel: + * Description: resizing input Tensor by input constants using Bilinear Interpolation + * 2 Inputs: Input tensor ( Shape dimensions count = 4); Input constant (Shape dimensions count = 1, + * Num elements =2) Parameters: align_corners; half_pixel_centers; + * + * Example: + * Input(2, 2, 2, 1) + * | + * | Constant Input(2) [3,3] INT32 + * | / + * ResizeBilinear + * | + * Output(2, 3, 3, 1) UINT8 + */ -void ResizeBilinear::configure() +void configure_kernel_CircleResizeBilinear(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - 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) + // Check of the size of input. Should be 2 + assert(cur_op->inputs()->size() == 2); + const auto input_index = cur_op->inputs()->operator[](0); + const auto size_index = cur_op->inputs()->operator[](1); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(size_index != -1); + assert(output_index != -1); + // Get tensors + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto size = runtime_graph->getCircleTensorByIndex(size_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + // Check of the Input shape + assert(kernels::getTensorShape(input).dimensionsCount() == 4); + // Check of the Const input size shape + assert(kernels::getTensorShape(size).dimensionsCount() == 1); + assert(Tensor::element_type(size) == DataType::S32); + assert(kernels::getTensorShape(size).dims(0) == 2); + + const auto *params = cur_op->builtin_options_as_ResizeBilinearOptions(); + if (params->half_pixel_centers() && params->align_corners()) assert(false && "If half_pixel_centers is True, align_corners must be False."); - LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2); - Shape output_shape(4); - output_shape.dim(0) = input()->shape().dim(0); - output_shape.dim(1) = getTensorData(size())[0]; - output_shape.dim(2) = getTensorData(size())[1]; - output_shape.dim(3) = input()->shape().dim(3); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); } -void ResizeBilinear::execute() const +void execute_kernel_CircleResizeBilinear(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - tflite::ResizeBilinearParams op_params{}; - op_params.align_corners = params().align_corners; - op_params.half_pixel_centers = params().half_pixel_centers; - switch (output()->element_type()) + assert(cur_op->inputs()->size() == 2); + const auto input_index = cur_op->inputs()->operator[](0); + const auto size_index = cur_op->inputs()->operator[](1); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(size_index != -1); + assert(output_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto size = runtime_graph->getCircleTensorByIndex(size_index); + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + + const uint8_t *input_data = runtime_graph->getDataByTensor(input); + const uint8_t *size_data = runtime_graph->getConstDataByTensor(size); + uint8_t *output_data = runtime_graph->getDataByTensor(output); + + assert(input_data != nullptr); + assert(size_data != nullptr); + assert(output_data != nullptr); + + // Get parameters + const auto *op_params = cur_op->builtin_options_as_ResizeBilinearOptions(); + + switch (Tensor::element_type(output)) { case DataType::FLOAT32: luci_interpreter_pal::ResizeBilinear( - op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), - getTensorData(size()), getTensorShape(output()), getTensorData(output())); + op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(size), kernels::getTensorData(size_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; case DataType::U8: luci_interpreter_pal::ResizeBilinear( - op_params, getTensorShape(input()), getTensorData(input()), getTensorShape(size()), - getTensorData(size()), getTensorShape(output()), getTensorData(output())); + op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(size), kernels::getTensorData(size_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; default: assert(false && "Unsupported type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.h b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.h index 9159b32..b7bdc2a 100644 --- a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.h +++ b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp index 933a112..d8ecae5 100644 --- a/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ResizeBilinear.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2023 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"); @@ -15,9 +15,11 @@ * limitations under the License. */ -#include "kernels/ResizeBilinear.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "loader/ModuleLoader.h" +#include "luci_interpreter/test_models/resize_bilinear/FloatResizeBilinearKernel.h" +#include "luci_interpreter/test_models/resize_bilinear/U8ResizeBilinearKernel.h" +#include "luci_interpreter/test_models/resize_bilinear/NegResizeBilinearKernel.h" namespace luci_interpreter { @@ -28,226 +30,159 @@ namespace using namespace testing; +class ResizeBilinearTest : public ::testing::Test +{ + // Do nothing +}; + template -void Check(std::initializer_list input_shape, std::initializer_list size_shape, - std::initializer_list output_shape, std::initializer_list input_data, - std::initializer_list size_data, std::initializer_list output_data, - bool align_corners, bool half_pixel_centers) +std::vector checkResizeBilinearKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - ResizeBilinearParams params{}; - params.align_corners = align_corners; - params.half_pixel_centers = half_pixel_centers; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -template <> -void Check(std::initializer_list input_shape, - std::initializer_list size_shape, - std::initializer_list output_shape, - std::initializer_list input_data, - std::initializer_list size_data, - std::initializer_list output_data, bool align_corners, - bool half_pixel_centers) +TEST_F(ResizeBilinearTest, Float_P) { - // On TFlite example use Uint8 value it self, so this means quant param scale 1.0f and zero - // point 0. - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = - makeInputTensor(input_shape, 1.0, 0, input_data, memory_manager.get()); - Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0, 0); - - ResizeBilinearParams params{}; - params.align_corners = align_corners; - params.half_pixel_centers = half_pixel_centers; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data, output_tensor.scale())); + test_kernel::TestDataFloatResizeBilinear test_data_kernel(false); + std::vector output_data_vector = checkResizeBilinearKernel(&test_data_kernel); + + EXPECT_THAT(output_data_vector, + FloatArrayNear(test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -template class ResizeBilinearTest : public ::testing::Test +TEST_F(ResizeBilinearTest, HalfPixelCenter_Float_P) { -}; -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(ResizeBilinearTest, DataTypes); + test_kernel::TestDataFloatResizeBilinear test_data_kernel(true); + std::vector output_data_vector = checkResizeBilinearKernel(&test_data_kernel); + + EXPECT_THAT(output_data_vector, + FloatArrayNear(test_data_kernel.get_output_data_by_index(0), 0.0001f)); +} + +TEST_F(ResizeBilinearTest, Uint8_P) +{ + test_kernel::TestDataUint8ResizeBilinear test_data_kernel(false); + std::vector output_data_vector = checkResizeBilinearKernel(&test_data_kernel); + + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} -TYPED_TEST(ResizeBilinearTest, SimpleTest) +TEST_F(ResizeBilinearTest, HalfPixelCenter_Uint8_P) { - Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // - }, - {3, 3}, - { - 3, 5, 6, // - 7, 9, 10, // - 9, 11, 12, // - 4, 8, 10, // - 8, 12, 14, // - 10, 14, 16, // - }, - false, false); - SUCCEED(); + test_kernel::TestDataUint8ResizeBilinear test_data_kernel(true); + std::vector output_data_vector = checkResizeBilinearKernel(&test_data_kernel); + + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST(ResizeBilinearTest, HalfPixelCenterFloatTest) +TEST_F(ResizeBilinearTest, InvalidInputShape_Float_NEG) { - Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, - { - 1, 2, // - 3, 4, // - 1, 2, // - 3, 4 // - }, - {3, 3}, - { - 1, 1.5, 2, // - 2, 2.5, 3, // - 3, 3.5, 4, // - 1, 1.5, 2, // - 2, 2.5, 3, // - 3, 3.5, 4, // - }, - false, true); - SUCCEED(); + + test_kernel::NegTestDataInvalidInputShapeFloatResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(ResizeBilinearTest, HalfPixelCenterUint8Test) +TEST_F(ResizeBilinearTest, InvalidParams_Float_NEG) { - Check({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 12, 16 // - }, - {3, 3}, - { - 2, 4, 6, // - 6, 7, 9, // - 9, 10, 12, // - 4, 7, 10, // - 8, 10, 13, // - 12, 14, 16, // - }, - false, true); - SUCCEED(); + + test_kernel::NegTestDataInvalidParamFloatResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(ResizeBilinearTest, InputShapeInvalid_NEG) +TEST_F(ResizeBilinearTest, InvalidSizeShape_Float_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = makeInputTensor({2, 2, 2}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // - }, - memory_manager.get()); - Tensor size_tensor = makeInputTensor({2}, {3, 3}, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - ResizeBilinearParams params{}; - params.align_corners = false; - params.half_pixel_centers = false; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + + test_kernel::NegTestDataInvalidSizeShapeDimensionsFloatResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(ResizeBilinearTest, SizeShapeInvalid_NEG) +TEST_F(ResizeBilinearTest, InvalidInputShape_uint8_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // - }, - memory_manager.get()); - Tensor size_tensor = makeInputTensor({2, 1}, {3, 3}, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - ResizeBilinearParams params{}; - params.align_corners = false; - params.half_pixel_centers = false; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + + test_kernel::NegTestDataInvalidInputShapeUint8ResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(ResizeBilinearTest, SizeDimInvalid_NEG) +TEST_F(ResizeBilinearTest, InvalidParams_uint8_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // - }, - memory_manager.get()); - Tensor size_tensor = makeInputTensor({3}, {3, 3, 1}, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - ResizeBilinearParams params{}; - params.align_corners = false; - params.half_pixel_centers = false; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + + test_kernel::NegTestDataInvalidParamUint8ResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST(ResizeBilinearTest, InvalidParams_NEG) +TEST_F(ResizeBilinearTest, InvalidSizeShape_uint8_NEG) { - std::unique_ptr memory_manager = std::make_unique(); - - Tensor input_tensor = makeInputTensor({2, 2, 2, 1}, - { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // - }, - memory_manager.get()); - Tensor size_tensor = makeInputTensor({2}, {3, 3}, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - ResizeBilinearParams params{}; - params.align_corners = true; - params.half_pixel_centers = true; - - ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + + test_kernel::NegTestDataInvalidSizeShapeDimensionsUint8ResizeBilinearKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace diff --git a/onert-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h b/onert-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h index d989490..137d031 100644 --- a/onert-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h +++ b/onert-micro/luci-interpreter/src/kernels/ResizeNearestNeighbor.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/ReverseV2.cpp b/onert-micro/luci-interpreter/src/kernels/ReverseV2.cpp index 44a7fae..76eadbd 100644 --- a/onert-micro/luci-interpreter/src/kernels/ReverseV2.cpp +++ b/onert-micro/luci-interpreter/src/kernels/ReverseV2.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/ReverseV2.h b/onert-micro/luci-interpreter/src/kernels/ReverseV2.h index 0965075..51211c7 100644 --- a/onert-micro/luci-interpreter/src/kernels/ReverseV2.h +++ b/onert-micro/luci-interpreter/src/kernels/ReverseV2.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Rsqrt.cpp b/onert-micro/luci-interpreter/src/kernels/Rsqrt.cpp index 2788b67..c45c3e4 100644 --- a/onert-micro/luci-interpreter/src/kernels/Rsqrt.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Rsqrt.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Rsqrt.h b/onert-micro/luci-interpreter/src/kernels/Rsqrt.h index c64aecd..adc5bcf 100644 --- a/onert-micro/luci-interpreter/src/kernels/Rsqrt.h +++ b/onert-micro/luci-interpreter/src/kernels/Rsqrt.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp b/onert-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp index 5567edb..3c64942 100644 --- a/onert-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Rsqrt.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/SISOKernel.h b/onert-micro/luci-interpreter/src/kernels/SISOKernel.h new file mode 100644 index 0000000..98e352a --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/SISOKernel.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_SISO_KERNEL_H +#define LUCI_INTERPRETER_KERNELS_SISO_KERNEL_H + +#include "Builders.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +// Single input single output kernel +class SISOKernel +{ +public: + SISOKernel(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) + { + const auto input_index = cur_op->inputs()->operator[](0); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input_index != -1); + assert(output_index != -1); + + _input_tensor = runtime_graph->getCircleTensorByIndex(input_index); + _output_tensor = runtime_graph->getCircleTensorByIndex(output_index); + + assert(_input_tensor != nullptr); + assert(_output_tensor != nullptr); + } + + const circle::Tensor *input() const { return _input_tensor; } + const circle::Tensor *output() const { return _output_tensor; } + +private: + const circle::Tensor *_input_tensor; + const circle::Tensor *_output_tensor; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SISO_KERNEL_H diff --git a/onert-micro/luci-interpreter/src/kernels/SVDF.h b/onert-micro/luci-interpreter/src/kernels/SVDF.h index 5141f45..335a6cd 100644 --- a/onert-micro/luci-interpreter/src/kernels/SVDF.h +++ b/onert-micro/luci-interpreter/src/kernels/SVDF.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/SVDF.test.cpp b/onert-micro/luci-interpreter/src/kernels/SVDF.test.cpp index fbeaf5d..82bd9b0 100644 --- a/onert-micro/luci-interpreter/src/kernels/SVDF.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SVDF.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Shape.cpp b/onert-micro/luci-interpreter/src/kernels/Shape.cpp index 2f16ac8..31a0b62 100644 --- a/onert-micro/luci-interpreter/src/kernels/Shape.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Shape.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,57 +14,33 @@ * limitations under the License. */ -#include "kernels/Shape.h" +#include "Builders.h" +#include "SISOKernel.h" #include "kernels/Utils.h" namespace luci_interpreter { -namespace kernels +void configure_kernel_CircleShape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - -ShapeKernel::ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams ¶ms) - : KernelWithParams({input}, {output}, params) -{ -} - -void ShapeKernel::configure() -{ - LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::S32 or - output()->element_type() == DataType::S64); - const auto input_shape = input()->shape(); - - Shape output_shape(1); - output_shape.dim(0) = input_shape.num_dims(); - // TODO: enable it only if kernel with dynamic shapes - output()->resize(output_shape); + kernels::SISOKernel kernel(cur_op, runtime_graph); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::S32); } -void ShapeKernel::execute() const +void execute_kernel_CircleShape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - switch (params().out_type) - { - case DataType::S32: - evalInt(); - break; - case DataType::S64: - evalInt(); - break; - default: - assert(false && "Unsupported type."); - } -} + kernels::SISOKernel kernel(cur_op, runtime_graph); -template void ShapeKernel::evalInt() const -{ - const auto input_shape = input()->shape(); + const circle::Tensor *input = kernel.input(); + const circle::Tensor *output = kernel.output(); - auto output_data = getTensorData(output()); + assert(Tensor::element_type(output) == DataType::S32); + int32_t *output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); - for (int i = 0; i < input_shape.num_dims(); ++i) + const int rank = Tensor::num_dims(input); + for (int i = 0; i < rank; ++i) { - output_data[i] = input_shape.dim(i); + output_data[i] = Tensor::dim(input, i); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Shape.test.cpp b/onert-micro/luci-interpreter/src/kernels/Shape.test.cpp index 19c5858..f20eb78 100644 --- a/onert-micro/luci-interpreter/src/kernels/Shape.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Shape.test.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +14,14 @@ * limitations under the License. */ -#include "kernels/Shape.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/shape/ShapeKernel.h" +#include "luci_interpreter/test_models/shape/NegShapeKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,61 +29,58 @@ using namespace testing; class ShapeTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -template void runShapeKernel(loco::DataType dataType, IMemoryManager *memory_manager) +template +std::vector checkShapeKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 3, 1, 3, 5}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); - Tensor output_tensor = makeOutputTensor(dataType); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - ShapeParams params{}; - params.out_type = dataType; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - ShapeKernel kernel(&input_tensor, &output_tensor, params); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + runtime_module.execute(); - std::vector ref_output_data{1, 3, 1, 3, 5}; - EXPECT_THAT(extractTensorData(output_tensor), ref_output_data); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - std::vector ref_output_shape{5}; - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + U *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(U)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(ShapeTest, OutTypeInt) +TEST_F(ShapeTest, MainTest_P) { - - // Run for int32_t output - runShapeKernel(loco::DataType::S32, _memory_manager.get()); - // Run for int64_t output - runShapeKernel(loco::DataType::S64, _memory_manager.get()); - - SUCCEED(); + test_kernel::TestDataShapeKernel test_data_shape_kernel; + std::vector output_data_vector = checkShapeKernel(&test_data_shape_kernel); + EXPECT_THAT(output_data_vector, test_data_shape_kernel.get_output_data_by_index(0)); } -TEST_F(ShapeTest, Invalid_Output_Type_NEG) +TEST_F(ShapeTest, Wrong_output_type_NEG) { - Shape input_shape{1, 3}; - - Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, ""); - Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32); - - ShapeParams params{}; - params.out_type = loco::DataType::FLOAT32; - - ShapeKernel kernel(&input_tensor, &output_tensor, params); - - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataWrongOutputTypeShapeKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Slice.cpp b/onert-micro/luci-interpreter/src/kernels/Slice.cpp index 7ae24bb..34e5498 100644 --- a/onert-micro/luci-interpreter/src/kernels/Slice.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Slice.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,140 +14,222 @@ * limitations under the License. */ -#include "kernels/Slice.h" -#include "Utils.h" -#include "PALSlice.h" +#include "Builders.h" +#include "kernels/Utils.h" +#include "MISOKernel.h" #include -#include namespace luci_interpreter { -namespace kernels +namespace { -const int max_dim = 4; +const int max_dim = 5; -Slice::Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output) - : Kernel({input, begin, size}, {output}) +struct SliceParams { -} + int8_t begin_count; + int32_t begin[5]; + int8_t size_count; + int32_t size[5]; +}; template -Shape calculateOutputShape(const Tensor *input, const Tensor *begin, const Tensor *size) +inline void slice(const luci_interpreter::SliceParams &op_params, + const luci_interpreter::RuntimeShape &input_shape, const T *input_data, + const luci_interpreter::RuntimeShape &output_shape, T *output_data) { - Shape output_shape = Shape(input->shape().num_dims()); - for (int idx = 0; idx < input->shape().num_dims(); idx++) + const luci_interpreter::RuntimeShape ext_shape = + luci_interpreter::RuntimeShape::extendedShape(5, input_shape); + const int begin_count = op_params.begin_count; + const int size_count = op_params.size_count; + // We front-pad the begin and size vectors. + int start[5]; + int stop[5]; + for (int i = 0; i < 5; ++i) { - T size_value = getTensorData(size)[idx]; - if (size_value < 0) - { - if (size_value != -1) - { - assert(false && "Invalid size."); - } - size_value = input->shape().dim(idx) - getTensorData(begin)[idx]; - } - else + int padded_i = 5 - i; + start[i] = begin_count < padded_i ? 0 : op_params.begin[begin_count - padded_i]; + stop[i] = (size_count < padded_i || op_params.size[size_count - padded_i] == -1) + ? ext_shape.dims(i) + : start[i] + op_params.size[size_count - padded_i]; + } + + for (int i0 = start[0]; i0 < stop[0]; ++i0) + { + for (int i1 = start[1]; i1 < stop[1]; ++i1) { - if (input->shape().dim(idx) < getTensorData(begin)[idx] + size_value) + for (int i2 = start[2]; i2 < stop[2]; ++i2) { - assert(false && "Invalid begin and size."); + for (int i3 = start[3]; i3 < stop[3]; ++i3) + { + for (int i4 = start[4]; i4 < stop[4]; ++i4) + { + auto position = + (((i0 * ext_shape.dims(1) + i1) * ext_shape.dims(2) + i2) * ext_shape.dims(3) + i3) * + ext_shape.dims(4) + + i4; + *output_data++ = input_data[position]; + } + } } } - output_shape.dim(idx) = static_cast(size_value); } - return output_shape; } template -void getBeginAndSizeVectors(int dimensions, const Tensor *begin, const Tensor *size, - std::vector *begins, std::vector *sizes) +void getBeginAndSizeVectors(int dimensions, const uint8_t *begin_data, const uint8_t *size_data, + int32_t *begins, int32_t *sizes) { - for (int idx = dimensions - 1; idx >= 0; --idx) + int offset = max_dim - dimensions; + for (int idx = 0; idx < dimensions; ++idx) { - begins->push_back(getTensorData(begin)[idx]); - sizes->push_back(getTensorData(size)[idx]); + begins[offset + idx] = kernels::getTensorData(begin_data)[idx]; + sizes[offset + idx] = kernels::getTensorData(size_data)[idx]; } } +} // namespace -void Slice::configure() +void configure_kernel_CircleSlice(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - assert(input()->element_type() == output()->element_type()); - assert(begin()->element_type() == DataType::S32 || begin()->element_type() == DataType::S64); - assert(size()->element_type() == DataType::S32 || size()->element_type() == DataType::S64); - assert(begin()->shape().num_dims() == 1); - assert(size()->shape().num_dims() == 1); - assert(input()->shape().num_dims() <= max_dim); - // TODO: enable it only if kernel with dynamic shapes - if (begin()->element_type() == DataType::S32) + kernels::MISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32 || + Tensor::element_type(kernel.input2()) == DataType::S64); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input3()) == DataType::S32 || + Tensor::element_type(kernel.input3()) == DataType::S64); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input2()) == 1); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input3()) == 1); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input1()) <= max_dim); +} + +void execute_kernel_CircleSlice(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) +{ + kernels::MISOKernel kernel(cur_op, runtime_graph); + + bool is_dynamic_shapes = false; + + const circle::Tensor *input = kernel.input1(); + const circle::Tensor *begin = kernel.input2(); + const circle::Tensor *size_tensor = kernel.input3(); + const circle::Tensor *output = kernel.output(); + + const auto *input_data = runtime_graph->getDataByTensor(input); + if (input_data == nullptr) + input_data = runtime_graph->getConstDataByTensor(input); + assert(input_data); + + const auto *begin_data = runtime_graph->getDataByTensor(begin); + if (begin_data == nullptr) { - output()->resize(calculateOutputShape(input(), begin(), size())); + begin_data = runtime_graph->getConstDataByTensor(begin); + is_dynamic_shapes = true; } - else if (begin()->element_type() == DataType::S64) + assert(begin_data); + + const auto *size_data = runtime_graph->getDataByTensor(size_tensor); + if (size_data == nullptr) { - output()->resize(calculateOutputShape(input(), begin(), size())); + size_data = runtime_graph->getConstDataByTensor(size_tensor); + is_dynamic_shapes = true; } - else + assert(size_data); + + auto *output_data = runtime_graph->getDataByTensor(output); + assert(output_data); + + SliceParams op_params{}; + op_params.begin_count = max_dim; + op_params.size_count = max_dim; + for (int i = 0; i < max_dim; i++) { - assert(false && "Unsupported type."); + op_params.begin[i] = 0; + op_params.size[i] = 1; } -} + auto num_dim = Tensor::num_dims(input); -void Slice::execute() const -{ - std::vector begins; - begins.reserve(max_dim); - std::vector sizes; - sizes.reserve(max_dim); - if (begin()->element_type() == DataType::S32) + if (Tensor::element_type(begin) == DataType::S32) { - getBeginAndSizeVectors(input()->shape().num_dims(), begin(), size(), &begins, &sizes); + getBeginAndSizeVectors(num_dim, begin_data, size_data, op_params.begin, + op_params.size); } - else if (begin()->element_type() == DataType::S64) + else if (Tensor::element_type(begin) == DataType::S64) { - getBeginAndSizeVectors(input()->shape().num_dims(), begin(), size(), &begins, &sizes); + getBeginAndSizeVectors(num_dim, begin_data, size_data, op_params.begin, + op_params.size); } else { - assert(false && "Unsupported begin type."); - } - for (int i = input()->shape().num_dims(); i < max_dim; ++i) - { - begins.push_back(0); - sizes.push_back(1); + assert(false && "Unsupported type"); } - assert(begins.size() == 4); - assert(sizes.size() == 4); - tflite::SliceParams op_params{}; - op_params.begin_count = 4; - op_params.size_count = 4; - for (int i = 0; i < 4; i++) +#ifndef DIS_DYN_SHAPES + if (is_dynamic_shapes) { - op_params.begin[i] = begins[3 - i]; - op_params.size[i] = sizes[3 - i]; + int32_t data_size = 1; + luci_interpreter::RuntimeShape dynamic_shapes(max_dim - num_dim + 1); + int offset = max_dim - Tensor::num_dims(input); + for (int i = 0; i <= max_dim - num_dim; ++i) + { + if (i + offset > 4) + return; + auto cur_size = op_params.size[i + offset] != -1 + ? op_params.size[i + offset] + : Tensor::dim(input, i) - op_params.begin[i + offset]; + data_size *= cur_size; + + dynamic_shapes.setDim(i, cur_size); + } + data_size *= size(Tensor::element_type(output)); + + runtime_graph->addDynamicShapeTensor(output, std::move(dynamic_shapes)); + + if (data_size == 0) + { + runtime_graph->resetTensorData(nullptr, output); + return; + } + + auto new_output_data = new uint8_t[data_size]; + output_data = new_output_data; + runtime_graph->resetTensorData(new_output_data, output); } - switch (input()->element_type()) +#else + assert(is_dynamic_shapes == false); +#endif // DIS_DYN_SHAPES + + switch (Tensor::element_type(input)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - luci_interpreter_pal::Slice(op_params, getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); + slice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), kernels::getTensorShape(output), + kernels::getTensorData(output_data)); break; +#endif // DIS_FLOAT +#ifndef DIS_QUANT case DataType::U8: - luci_interpreter_pal::Slice(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + slice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), kernels::getTensorShape(output), + kernels::getTensorData(output_data)); break; case DataType::S8: - luci_interpreter_pal::Slice(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + slice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), kernels::getTensorShape(output), + kernels::getTensorData(output_data)); + break; + case DataType::S16: + slice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), kernels::getTensorShape(output), + kernels::getTensorData(output_data)); break; +#endif // DIS_QUANT default: assert(false && "Unsupported input type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Slice.h b/onert-micro/luci-interpreter/src/kernels/Slice.h deleted file mode 100644 index 5b41562..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Slice.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_SLICE_H -#define LUCI_INTERPRETER_KERNELS_SLICE_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Slice : public Kernel -{ -public: - Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *begin() const { return _inputs[1]; } - const Tensor *size() const { return _inputs[2]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_SLICE_H diff --git a/onert-micro/luci-interpreter/src/kernels/Slice.test.cpp b/onert-micro/luci-interpreter/src/kernels/Slice.test.cpp index 857dd17..0bd13d7 100644 --- a/onert-micro/luci-interpreter/src/kernels/Slice.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Slice.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,57 +14,126 @@ * limitations under the License. */ -#include "kernels/Slice.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/slice/FloatSliceKernel.h" +#include "luci_interpreter/test_models/slice/QuantU8SliceKernel.h" +#include "luci_interpreter/test_models/slice/QuantS16SliceKernel.h" +#include "luci_interpreter/test_models/slice/NegSliceKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -template class SliceTest : public ::testing::Test +class SliceTest : public ::testing::Test { + // Do nothing }; -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(SliceTest, DataTypes); +template std::vector checkSliceKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -TYPED_TEST(SliceTest, SimpleTest) + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} + +TEST_F(SliceTest, Float_P) { - std::unique_ptr memory_manager = std::make_unique(); - - std::vector input_data{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6}; - Shape input_shape{3, 2, 3, 1}; - std::vector begin_data{1, 0, 0, 0}; - Shape begin_shape{4}; - std::vector size_data{2, 1, -1, 1}; - Shape size_shape{4}; - std::vector output_data{3, 3, 3, 5, 5, 5}; - std::vector output_shape{2, 1, 3, 1}; - - Tensor input_tensor = - makeInputTensor()>(input_shape, input_data, memory_manager.get()); - Tensor begin_tensor = - makeInputTensor(begin_shape, begin_data, memory_manager.get()); - Tensor size_tensor = makeInputTensor(size_shape, size_data, memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(getElementType()); - - Slice kernel(&input_tensor, &begin_tensor, &size_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), - ::testing::ElementsAreArray(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); + test_kernel::TestDataFloatSlice test_data_kernel; + std::vector output_data_vector = checkSliceKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } +TEST_F(SliceTest, U8_P) +{ + test_kernel::TestDataU8Slice test_data_kernel; + std::vector output_data_vector = checkSliceKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} + +TEST_F(SliceTest, INT16_P) +{ + test_kernel::TestDataS16Slice test_data_kernel; + std::vector output_data_vector = checkSliceKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} + +TEST_F(SliceTest, TypeMismatch_NEG) +{ + test_kernel::TestDataTypeMismatchSlice test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(SliceTest, WrongBeginType_NEG) +{ + test_kernel::TestDataWrongBeginTypeSlice test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(SliceTest, WrongSizeType_NEG) +{ + test_kernel::TestDataWrongSizeTypeSlice test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +TEST_F(SliceTest, WrongInputShape_NEG) +{ + test_kernel::TestDataWrongInputShapeSlice test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); +} + +// TODO: add S8 test + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Softmax.cpp b/onert-micro/luci-interpreter/src/kernels/Softmax.cpp index cfc1dfc..4647cc9 100644 --- a/onert-micro/luci-interpreter/src/kernels/Softmax.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Softmax.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -17,8 +16,8 @@ #include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include #include "PALSoftmax.h" namespace luci_interpreter @@ -34,97 +33,48 @@ void evalFloat(const circle::Tensor *input, const circle::Tensor *output, const auto *input_data = runtime_graph->getDataByTensor(input); auto *output_data = runtime_graph->getDataByTensor(output); - tflite::SoftmaxParams op_params{}; - op_params.beta = options->beta(); - - tflite::reference_ops::Softmax( - op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); + luci_interpreter_pal::Softmax(options->beta(), kernels::getTensorShape(input), + kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); } #endif // DIS_FLOAT -#ifndef DIS_QUANT -template -void evalQuantized(const circle::Tensor *input, const circle::Tensor *output, - const circle::SoftmaxOptions *options, BaseRuntimeGraph *runtime_graph) -{ - // TODO: Enable it - assert(false && "Not impl yet"); - - const auto *input_data = runtime_graph->getDataByTensor(input); - auto *output_data = runtime_graph->getDataByTensor(output); - - tflite::SoftmaxParams op_params{}; - - luci_interpreter_pal::InitializeParams(&op_params, Tensor::scale(input), options->beta()); - luci_interpreter_pal::Softmax( - op_params, kernels::getTensorShape(input), kernels::getTensorData(input_data), - kernels::getTensorShape(output), kernels::getTensorData(output_data)); -} -#endif - } // namespace void configure_kernel_CircleSoftmax(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto input_index = cur_op->inputs()->operator[](0); - const auto output_index = cur_op->outputs()->operator[](0); - - assert(input_index != -1); - assert(output_index != -1); - - const auto input = runtime_graph->getCircleTensorByIndex(input_index); - auto output = runtime_graph->getCircleTensorByIndex(output_index); + kernels::SISOKernel kernel(cur_op, runtime_graph); - assert(input != nullptr); - assert(output != nullptr); - - LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == Tensor::element_type(output)); - LUCI_INTERPRETER_CHECK(Tensor::num_dims(input) >= 1); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input()) >= 1); #ifndef DIS_QUANT - if (Tensor::element_type(input) == DataType::U8 || Tensor::element_type(input) == DataType::S8) + if (Tensor::element_type(kernel.input()) == DataType::U8 || + Tensor::element_type(kernel.input()) == DataType::S8) { - LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == DataType::S8 || - Tensor::zero_point(output) == 0); - LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == DataType::U8 || - Tensor::zero_point(output) == std::numeric_limits::min()); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == DataType::S8 || + Tensor::zero_point(kernel.output()) == 0); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == DataType::U8 || + Tensor::zero_point(kernel.output()) == + std::numeric_limits::min()); } #endif } -void execute_kernel_CircleSoftmax(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, - bool) +void execute_kernel_CircleSoftmax(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto input_index = cur_op->inputs()->operator[](0); - const auto output_index = cur_op->outputs()->operator[](0); - - assert(input_index != -1); - assert(output_index != -1); - - const auto input = runtime_graph->getCircleTensorByIndex(input_index); - auto output = runtime_graph->getCircleTensorByIndex(output_index); - - assert(input != nullptr); - assert(output != nullptr); + kernels::SISOKernel kernel(cur_op, runtime_graph); const auto *options = cur_op->builtin_options_as_SoftmaxOptions(); - switch (Tensor::element_type(input)) + switch (Tensor::element_type(kernel.input())) { #ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(input, output, options, runtime_graph); + evalFloat(kernel.input(), kernel.output(), options, runtime_graph); break; #endif // DIS_FLOAT -#ifndef DIS_QUANT - case DataType::S8: - evalQuantized(input, output, options, runtime_graph); - break; - case DataType::U8: - evalQuantized(input, output, options, runtime_graph); - break; -#endif // DIS_QUANT default: assert(false && "Unsupported type."); } diff --git a/onert-micro/luci-interpreter/src/kernels/Softmax.test.cpp b/onert-micro/luci-interpreter/src/kernels/Softmax.test.cpp index d9656e5..f026e89 100644 --- a/onert-micro/luci-interpreter/src/kernels/Softmax.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Softmax.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.h b/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.h index 12640ca..0893003 100644 --- a/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.h +++ b/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp b/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp index aaf4e47..3a8b0a8 100644 --- a/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp index 3fab402..06cc5fa 100644 --- a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.h b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.h index b9d9c38..e66316b 100644 --- a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.h +++ b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp index 724abd0..4af4886 100644 --- a/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SpaceToDepth.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Split.cpp b/onert-micro/luci-interpreter/src/kernels/Split.cpp index 9737a01..70513a5 100644 --- a/onert-micro/luci-interpreter/src/kernels/Split.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Split.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,68 +14,80 @@ * limitations under the License. */ -#include "Split.h" - +#include "Builders.h" #include "Utils.h" - -#include "PALSplit.h" +#include "Split.h" namespace luci_interpreter { -namespace kernels -{ -Split::Split(const Tensor *axis, const Tensor *input, std::vector outputs) - : Kernel({axis, input}, std::move(outputs)) +void configure_kernel_CircleSplit(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { -} + const auto input_index = cur_op->inputs()->operator[](0); + const auto axis_index = cur_op->inputs()->operator[](1); -void Split::configure() -{ - assert(axis()->shape().num_elements() == 1); - _axis_value = getTensorData(axis())[0]; - if (_axis_value < 0) - _axis_value += input()->shape().num_dims(); - assert(_axis_value >= 0 && _axis_value < input()->shape().num_dims()); - - const int32_t input_size = input()->shape().dim(_axis_value); - assert(input_size % _outputs.size() == 0); - const int32_t slice_size = input_size / _outputs.size(); - // TODO: enable it only if kernel with dynamic shapes - Shape output_shape = input()->shape(); - output_shape.dim(_axis_value) = slice_size; - for (Tensor *output : _outputs) - { - output->resize(output_shape); - } + LUCI_INTERPRETER_CHECK(input_index != -1); + LUCI_INTERPRETER_CHECK(axis_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto axis = runtime_graph->getCircleTensorByIndex(axis_index); + + LUCI_INTERPRETER_CHECK(input != nullptr); + LUCI_INTERPRETER_CHECK(axis != nullptr); } -void Split::execute() const +void execute_kernel_CircleSplit(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - tflite::SplitParams params{}; - params.num_split = _outputs.size(); - params.axis = _axis_value; - -#define TF_LITE_SPLIT(scalar) \ - { \ - VectorOfTensors all_outputs(_outputs); \ - luci_interpreter_pal::Split(params, getTensorShape(input()), getTensorData(input()), \ - all_outputs.shapes(), all_outputs.data()); \ - } + const auto input_index = cur_op->inputs()->operator[](1); + const auto axis_index = cur_op->inputs()->operator[](0); + + assert(input_index != -1); + assert(axis_index != -1); + + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto axis = runtime_graph->getCircleTensorByIndex(axis_index); + + assert(input != nullptr); + assert(axis != nullptr); + + const auto *axis_data = runtime_graph->getDataByTensor(axis); + if (axis_data == nullptr) + axis_data = runtime_graph->getConstDataByTensor(axis); + + assert(axis_data); + + int32_t axis_value = (kernels::getTensorData(axis_data))[0]; + if (axis_value < 0) + axis_value += Tensor::num_dims(input); + + assert(axis_value >= 0); + assert(axis_value < Tensor::num_dims(input)); - switch (input()->element_type()) + switch (Tensor::element_type(input)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - TF_LITE_SPLIT(float); - break; - case DataType::U8: - TF_LITE_SPLIT(uint8_t); - break; + { + return splitImpl(cur_op, input, axis_value, runtime_graph); + } +#endif // DIS_FLOAT +#ifndef DIS_QUANT + case DataType::S8: + { + return splitImpl(cur_op, input, axis_value, runtime_graph); + } + case DataType::S16: + { + return splitImpl(cur_op, input, axis_value, runtime_graph); + } +#endif // DIS_QUANT + case DataType::S32: + { + return splitImpl(cur_op, input, axis_value, runtime_graph); + } default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -#undef TF_LITE_SPLIT } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Split.h b/onert-micro/luci-interpreter/src/kernels/Split.h index 1892a74..99ffae8 100644 --- a/onert-micro/luci-interpreter/src/kernels/Split.h +++ b/onert-micro/luci-interpreter/src/kernels/Split.h @@ -1,6 +1,5 @@ /* * 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. @@ -15,34 +14,68 @@ * limitations under the License. */ -#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_H -#define LUCI_INTERPRETER_KERNELS_SPLIT_H +#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_IMPL_H +#define LUCI_INTERPRETER_KERNELS_SPLIT_IMPL_H -#include "core/Kernel.h" -#include "core/KernelParams.h" +#include "Builders.h" +#include "Utils.h" namespace luci_interpreter { -namespace kernels -{ -class Split : public Kernel +template +void splitImpl(const circle::Operator *cur_op, const circle::Tensor *input, int axis_value, + BaseRuntimeGraph *runtime_graph) { -public: - Split(const Tensor *axis, const Tensor *input, std::vector outputs); + const int output_count = cur_op->outputs()->size(); + + const auto output0_index = cur_op->outputs()->operator[](0); + assert(output0_index != -1); + + const auto output0 = runtime_graph->getCircleTensorByIndex(output0_index); + assert(output0 != nullptr); + + const int split_dimensions = Tensor::num_dims(input); + + assert(axis_value < split_dimensions); + assert(Tensor::num_dims(output0) == split_dimensions); + + int64_t outer_size = 1; + for (int i = 0; i < axis_value; ++i) + { + outer_size *= Tensor::dim(input, i); + } + + int64_t base_inner_size = 1; + for (int i = axis_value + 1; i < split_dimensions; ++i) + { + base_inner_size *= Tensor::dim(input, i); + } - const Tensor *axis() const { return _inputs[0]; } - const Tensor *input() const { return _inputs[1]; } - Tensor *output(int index) const { return _outputs[index]; } + const T *input_ptr = kernels::getTensorData(runtime_graph->getDataByTensor(input)); + assert(input_ptr != nullptr); + for (int k = 0; k < outer_size; ++k) + { + for (int i = 0; i < output_count; ++i) + { + const auto output_index = cur_op->outputs()->operator[](i); + assert(output_index != -1); - void configure() override; - void execute() const override; + const auto output = runtime_graph->getCircleTensorByIndex(output_index); + assert(output != nullptr); -private: - int32_t _axis_value{}; -}; + T *output_data = kernels::getTensorData(runtime_graph->getDataByTensor(output)); + assert(output_data != nullptr); + const int copy_size = Tensor::dim(output, axis_value) * base_inner_size; + T *output_ptr = output_data + k * copy_size; + assert(output_ptr != nullptr); + for (int j = 0; j < copy_size; ++j) + output_ptr[j] = input_ptr[j]; + input_ptr += copy_size; + } + } +} -} // namespace kernels } // namespace luci_interpreter -#endif // LUCI_INTERPRETER_KERNELS_SPLIT_H +#endif // LUCI_INTERPRETER_KERNELS_SPLIT_IMPL_H diff --git a/onert-micro/luci-interpreter/src/kernels/Split.test.cpp b/onert-micro/luci-interpreter/src/kernels/Split.test.cpp index 283cd9a..c091903 100644 --- a/onert-micro/luci-interpreter/src/kernels/Split.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Split.test.cpp @@ -15,115 +15,85 @@ * limitations under the License. */ -#include "kernels/Split.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/split/FloatSplitKernel.h" +#include "luci_interpreter/test_models/split/IntSplitKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; +class SplitTest : public ::testing::Test +{ + // Do nothing +}; + template -void Check(int axis, int num_splits, std::initializer_list input_shape, - std::initializer_list output_shape, std::initializer_list input_data, - std::vector> output_data) +std::vector> checkSplitKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - constexpr DataType element_type = getElementType(); - Tensor axis_tensor = makeInputTensor({}, {axis}, memory_manager.get()); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - std::vector output_tensors; - output_tensors.reserve(num_splits); - for (int i = 0; i < num_splits; ++i) - { - output_tensors.emplace_back(makeOutputTensor(element_type)); - } + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - std::vector output_tensor_ptrs(num_splits); - for (int i = 0; i < num_splits; ++i) + // Set input data { - output_tensor_ptrs[i] = &output_tensors[i]; + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - Split kernel(&axis_tensor, &input_tensor, std::move(output_tensor_ptrs)); - kernel.configure(); - for (int i = 0; i < num_splits; ++i) - { - memory_manager->allocate_memory(output_tensors[i]); - } - kernel.execute(); + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 2); + + std::vector> result; - for (int i = 0; i < num_splits; ++i) + for (int i = 0; i < 2; ++i) { - EXPECT_THAT(extractTensorData(output_tensors[i]), - ::testing::ElementsAreArray(output_data[i])); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(i)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(i) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + result.push_back(output_data_vector); } + + return result; } -template class SplitTest : public ::testing::Test +TEST_F(SplitTest, Float_P) { -}; - -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(SplitTest, DataTypes); + test_kernel::TestDataFloatSplit test_data_kernel; + const auto output_data_vector = checkSplitKernel(&test_data_kernel); -TYPED_TEST(SplitTest, FourDimensional) -{ - Check(/*axis=*/0, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - { - {1, 2, 3, 4, 5, 6, 7, 8}, // - {9, 10, 11, 12, 13, 14, 15, 16}, // - }); - Check( - /*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - { - {1, 2, 3, 4, 9, 10, 11, 12}, // - {5, 6, 7, 8, 13, 14, 15, 16}, // - }); - Check( - /*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - { - {1, 2, 5, 6, 9, 10, 13, 14}, // - {3, 4, 7, 8, 11, 12, 15, 16}, // - }); - Check( - /*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - { - {1, 3, 5, 7, 9, 11, 13, 15}, // - {2, 4, 6, 8, 10, 12, 14, 16}, // - }); + for (int i = 0; i < 2; ++i) + { + EXPECT_THAT(output_data_vector[i], test_data_kernel.get_output_data_by_index(i)); + } } -TYPED_TEST(SplitTest, OneDimensional) +TEST_F(SplitTest, Int_P) { - Check( - /*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8}, - {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); -} + test_kernel::TestDataIntSplit test_data_kernel; + const auto output_data_vector = checkSplitKernel(&test_data_kernel); -TYPED_TEST(SplitTest, NegativeAxis) -{ - Check( - /*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - { - {1, 2, 3, 4, 5, 6, 7, 8}, // - {9, 10, 11, 12, 13, 14, 15, 16}, - }); + for (int i = 0; i < 2; ++i) + { + EXPECT_THAT(output_data_vector[i], test_data_kernel.get_output_data_by_index(i)); + } } +// TODO: add negative tests? + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/SplitV.cpp b/onert-micro/luci-interpreter/src/kernels/SplitV.cpp index f01cf10..b78a394 100644 --- a/onert-micro/luci-interpreter/src/kernels/SplitV.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SplitV.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2023 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. @@ -15,99 +15,78 @@ * limitations under the License. */ -#include "SplitV.h" - +#include "Builders.h" #include "Utils.h" - -#include +#include "Split.h" namespace luci_interpreter { -namespace kernels -{ -SplitV::SplitV(const Tensor *input, const Tensor *size_splits, const Tensor *axis, - std::vector outputs) - : Kernel({input, size_splits, axis}, std::move(outputs)) +void configure_kernel_CircleSplitV(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + const auto axis_index = cur_op->inputs()->operator[](2); + LUCI_INTERPRETER_CHECK(axis_index != -1); + + const auto axis = runtime_graph->getCircleTensorByIndex(axis_index); + LUCI_INTERPRETER_CHECK(axis != nullptr); + + // Dynamic output tensors are needed if axis tensor is not constant + // Now doesn't support dynamic shapes for SplitV + LUCI_INTERPRETER_CHECK(runtime_graph->getConstDataByTensor(axis) != nullptr); } -void SplitV::configure() +void execute_kernel_CircleSplitV(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - assert(axis()->shape().num_elements() == 1); - _axis_value = getTensorData(axis())[0]; - if (_axis_value < 0) - _axis_value += input()->shape().num_dims(); - assert(_axis_value >= 0 && _axis_value < input()->shape().num_dims()); + const auto input_index = cur_op->inputs()->operator[](0); + const auto axis_index = cur_op->inputs()->operator[](2); + + assert(input_index != -1); + assert(axis_index != -1); - auto num_split = static_cast(_outputs.size()); - auto sizes_data = getTensorData(size_splits()); + const auto input = runtime_graph->getCircleTensorByIndex(input_index); + const auto axis = runtime_graph->getCircleTensorByIndex(axis_index); - assert(size_splits()->shape().num_dims() == 1); + assert(input != nullptr); + assert(axis != nullptr); - int32_t sum = 0; - const auto num_dims_size_spits = size_splits()->shape().dim(0); - int32_t count_neg_dim = 0; + const auto *axis_data = runtime_graph->getDataByTensor(axis); + if (axis_data == nullptr) + axis_data = runtime_graph->getConstDataByTensor(axis); - for (int32_t i = 0; i < num_dims_size_spits - 1; ++i) + assert(axis_data); + + int32_t axis_value = (kernels::getTensorData(axis_data))[0]; + if (axis_value < 0) + axis_value += Tensor::num_dims(input); + + assert(axis_value >= 0); + assert(axis_value < Tensor::num_dims(input)); + + switch (Tensor::element_type(input)) { - if (sizes_data[i] != -1) +#ifndef DIS_FLOAT + case DataType::FLOAT32: { - sum += sizes_data[i]; + return splitImpl(cur_op, input, axis_value, runtime_graph); } - else +#endif // DIS_FLOAT +#ifndef DIS_QUANT + case DataType::S8: { - count_neg_dim++; + return splitImpl(cur_op, input, axis_value, runtime_graph); } - } - assert(count_neg_dim < 2); - assert(size_splits()->shape().num_elements() == num_split); - - // TODO: enable it only if kernel with dynamic shapes - auto output_shape = input()->shape(); - for (int32_t i = 0; i < num_split; ++i) - { - if (sizes_data[i] == -1) + case DataType::S16: { - output_shape.dim(_axis_value) = input()->shape().dim(_axis_value) - sum; + return splitImpl(cur_op, input, axis_value, runtime_graph); } - else +#endif // DIS_QUANT + case DataType::S32: { - output_shape.dim(_axis_value) = sizes_data[i]; + return splitImpl(cur_op, input, axis_value, runtime_graph); } - _outputs[i]->resize(output_shape); - } -} - -void SplitV::execute() const -{ - tflite::SplitParams params{}; - params.num_split = _outputs.size(); - params.axis = _axis_value; - -#define TF_LITE_SPLIT(scalar) \ - { \ - VectorOfTensors all_outputs(_outputs); \ - tflite::optimized_ops::Split(params, getTensorShape(input()), getTensorData(input()), \ - all_outputs.shapes(), all_outputs.data()); \ - } - - switch (input()->element_type()) - { - case DataType::FLOAT32: - TF_LITE_SPLIT(float); - break; - case DataType::U8: - TF_LITE_SPLIT(uint8_t); - break; - case DataType::S16: - TF_LITE_SPLIT(int16_t); - break; default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } -#undef TF_LITE_SPLIT } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/SplitV.h b/onert-micro/luci-interpreter/src/kernels/SplitV.h deleted file mode 100644 index 0f9d312..0000000 --- a/onert-micro/luci-interpreter/src/kernels/SplitV.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_V_H -#define LUCI_INTERPRETER_KERNELS_SPLIT_V_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class SplitV : public Kernel -{ -public: - SplitV(const Tensor *input, const Tensor *size_splits, const Tensor *axis, - std::vector outputs); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *size_splits() const { return _inputs[1]; } - const Tensor *axis() const { return _inputs[2]; } - Tensor *output(int index) const { return _outputs[index]; } - - void configure() override; - void execute() const override; - -private: - int32_t _axis_value{}; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_SPLIT_V_H diff --git a/onert-micro/luci-interpreter/src/kernels/SplitV.test.cpp b/onert-micro/luci-interpreter/src/kernels/SplitV.test.cpp index 035bc21..d2e7d19 100644 --- a/onert-micro/luci-interpreter/src/kernels/SplitV.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/SplitV.test.cpp @@ -15,98 +15,73 @@ * limitations under the License. */ -#include "kernels/SplitV.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/split_v/SplitVKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; +class SplitVTest : public ::testing::Test +{ + // Do nothing +}; + template -void Check(int axis, std::initializer_list splits_size, - std::initializer_list input_shape, std::initializer_list input_data, - std::vector> output_data) +std::vector> checkSplitKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - - auto num_splits = static_cast(splits_size.size()); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor sizes_tensor = - makeInputTensor({num_splits}, splits_size, memory_manager.get()); - Tensor axis_tensor = makeInputTensor({}, {axis}, memory_manager.get()); - - std::vector output_tensors; - output_tensors.reserve(num_splits); - for (int i = 0; i < num_splits; ++i) - { - output_tensors.emplace_back(makeOutputTensor(element_type)); - } + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - std::vector output_tensor_ptrs(num_splits); - for (int i = 0; i < num_splits; ++i) - { - output_tensor_ptrs[i] = &output_tensors[i]; - } + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - SplitV kernel(&input_tensor, &sizes_tensor, &axis_tensor, std::move(output_tensor_ptrs)); - kernel.configure(); - for (int i = 0; i < num_splits; ++i) + // Set input data { - memory_manager->allocate_memory(output_tensors[i]); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - kernel.execute(); - for (int i = 0; i < num_splits; ++i) + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 3); + + std::vector> result; + + for (int i = 0; i < 3; ++i) { - auto tmp = extractTensorData(output_tensors[i]); - EXPECT_THAT(extractTensorData(output_tensors[i]), - ::testing::ElementsAreArray(output_data[i])); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(i)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(i) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + result.push_back(output_data_vector); } + + return result; } -template class SplitVTest : public ::testing::Test +TEST_F(SplitVTest, MainTest_P) { -}; + test_kernel::TestDataSplitVKernel test_data_kernel; + const auto output_data_vector = checkSplitKernel(&test_data_kernel); -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(SplitVTest, DataTypes); - -TYPED_TEST(SplitVTest, ThreeDimensional) -{ - Check( - /*axis=*/0, /*splits_size=*/{1, 2}, {3, 3, 3}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, - { - {1, 2, 3, 4, 5, 6, 7, 8, 9}, // - {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27} // - }); - Check( - /*axis=*/1, /*splits_size=*/{1, 2}, {3, 3, 3}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, - { - {1, 2, 3, 10, 11, 12, 19, 20, 21}, // - {4, 5, 6, 7, 8, 9, 13, 14, 15, 16, 17, 18, 22, 23, 24, 25, 26, 27} // - }); - Check( - /*axis=*/2, /*splits_size=*/{1, 2}, {3, 3, 3}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, - { - {1, 4, 7, 10, 13, 16, 19, 22, 25}, // - {2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27} // - }); + for (int i = 0; i < 3; ++i) + { + EXPECT_THAT(output_data_vector[i], test_data_kernel.get_output_data_by_index(i)); + } } +// TODO: add negative tests? + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Sqrt.cpp b/onert-micro/luci-interpreter/src/kernels/Sqrt.cpp index 3c1bc03..eed50df 100644 --- a/onert-micro/luci-interpreter/src/kernels/Sqrt.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Sqrt.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Sqrt.h b/onert-micro/luci-interpreter/src/kernels/Sqrt.h index d170234..4034655 100644 --- a/onert-micro/luci-interpreter/src/kernels/Sqrt.h +++ b/onert-micro/luci-interpreter/src/kernels/Sqrt.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Sqrt.test.cpp b/onert-micro/luci-interpreter/src/kernels/Sqrt.test.cpp index 914b3af..96835fb 100644 --- a/onert-micro/luci-interpreter/src/kernels/Sqrt.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Sqrt.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Square.cpp b/onert-micro/luci-interpreter/src/kernels/Square.cpp index 39c0d06..6386b91 100644 --- a/onert-micro/luci-interpreter/src/kernels/Square.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Square.cpp @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Square.h b/onert-micro/luci-interpreter/src/kernels/Square.h index 0277b4e..73ed5a7 100644 --- a/onert-micro/luci-interpreter/src/kernels/Square.h +++ b/onert-micro/luci-interpreter/src/kernels/Square.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/SquaredDifference.h b/onert-micro/luci-interpreter/src/kernels/SquaredDifference.h index 8aac9eb..9327caf 100644 --- a/onert-micro/luci-interpreter/src/kernels/SquaredDifference.h +++ b/onert-micro/luci-interpreter/src/kernels/SquaredDifference.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/onert-micro/luci-interpreter/src/kernels/Squeeze.test.cpp b/onert-micro/luci-interpreter/src/kernels/Squeeze.test.cpp index ace02b5..1bc0b64 100644 --- a/onert-micro/luci-interpreter/src/kernels/Squeeze.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Squeeze.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/StridedSlice.cpp b/onert-micro/luci-interpreter/src/kernels/StridedSlice.cpp index 654fd3c..3968fb9 100644 --- a/onert-micro/luci-interpreter/src/kernels/StridedSlice.cpp +++ b/onert-micro/luci-interpreter/src/kernels/StridedSlice.cpp @@ -15,135 +15,122 @@ * limitations under the License. */ -#include "kernels/StridedSlice.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "MISOKernel.h" -#include +#include "PALStridedSlice.h" namespace luci_interpreter { - -namespace kernels +namespace { -StridedSlice::StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, - const Tensor *strides, Tensor *output, const StridedSliceParams ¶ms) - : KernelWithParams({input, begin, end, strides}, {output}, params) +luci_interpreter_pal::StridedSliceParams +buildStridedSliceParams(int32_t dims, const int32_t *begin, const int32_t *end, + const int32_t *strides, const circle::StridedSliceOptions *options) { -} + luci_interpreter_pal::StridedSliceParams op_params; + op_params.start_indices_count = dims; + op_params.stop_indices_count = dims; + op_params.strides_count = dims; -void StridedSlice::configure() -{ - assert(begin()->shape().num_dims() == 1); - assert(end()->shape().num_dims() == 1); - assert(strides()->shape().num_dims() == 1); - assert(input()->element_type() == output()->element_type()); - assert(begin()->element_type() == DataType::S32); - assert(end()->element_type() == DataType::S32); - assert(strides()->element_type() == DataType::S32); - assert(input()->shape().num_dims() <= 4); - if (params().ellipsis_mask != 0) - { - assert(false && "ellipsis_mask is not implemented yet."); - } - if (params().new_axis_mask != 0) - { - assert(false && "new_axis_mask is not implemented yet."); - } - if (input()->element_type() == DataType::U8) + for (int i = 0; i < dims; ++i) { - assert(input()->scale() == output()->scale()); - assert(input()->zero_point() == output()->zero_point()); + op_params.start_indices[i] = begin[i]; + op_params.stop_indices[i] = end[i]; + op_params.strides[i] = strides[i]; } - tflite::StridedSliceParams op_params{}; - op_params.start_indices_count = input()->shape().num_dims(); - op_params.stop_indices_count = input()->shape().num_dims(); - op_params.strides_count = input()->shape().num_dims(); - for (int i = 0; i < input()->shape().num_dims(); i++) - { - op_params.start_indices[i] = getTensorData(begin())[i]; - op_params.stop_indices[i] = getTensorData(end())[i]; - op_params.strides[i] = getTensorData(strides())[i]; - } - op_params.begin_mask = params().begin_mask; + op_params.begin_mask = options->begin_mask(); op_params.ellipsis_mask = 0; - op_params.end_mask = params().end_mask; + op_params.end_mask = options->end_mask(); op_params.new_axis_mask = 0; - op_params.shrink_axis_mask = params().shrink_axis_mask; - std::vector output_shape_vector; - for (int i = 0; i < input()->shape().num_dims(); i++) - { - int idx = input()->shape().num_dims() - i - 1; - int32_t stride = getTensorData(strides())[idx]; - assert(stride != 0); - int32_t begin = ::tflite::strided_slice::StartForAxis(op_params, getTensorShape(input()), idx); - int32_t end = - ::tflite::strided_slice::StopForAxis(op_params, getTensorShape(input()), idx, begin); - - const bool shrink_axis = params().shrink_axis_mask & (1 << idx); - if (shrink_axis) - { - end = begin + 1; - } - - int32_t dim_shape = std::ceil((end - begin) / static_cast(stride)); - dim_shape = dim_shape < 0 ? 0 : dim_shape; - if (!shrink_axis) - { - output_shape_vector.push_back(dim_shape); - } - } - // TODO: enable it only if kernel with dynamic shapes - Shape output_shape = Shape(output_shape_vector.size()); - for (size_t i = 0; i < output_shape_vector.size(); i++) - { - output_shape.dim(i) = output_shape_vector[output_shape_vector.size() - i - 1]; - } - output()->resize(output_shape); + op_params.shrink_axis_mask = options->shrink_axis_mask(); + return op_params; } -void StridedSlice::execute() const +} // namespace + +void configure_kernel_CircleStridedSlice(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { - tflite::StridedSliceParams op_params{}; - op_params.start_indices_count = input()->shape().num_dims(); - op_params.stop_indices_count = input()->shape().num_dims(); - op_params.strides_count = input()->shape().num_dims(); + kernels::MISOKernel miso_kernel(cur_op, runtime_graph); - for (int i = 0; i < input()->shape().num_dims(); i++) - { - op_params.start_indices[i] = getTensorData(begin())[i]; - op_params.stop_indices[i] = getTensorData(end())[i]; - op_params.strides[i] = getTensorData(strides())[i]; - } - op_params.begin_mask = params().begin_mask; - op_params.ellipsis_mask = 0; - op_params.end_mask = params().end_mask; - op_params.new_axis_mask = 0; - op_params.shrink_axis_mask = params().shrink_axis_mask; + const circle::Tensor *input = miso_kernel.input1(); + const circle::Tensor *begin = miso_kernel.input2(); + const circle::Tensor *end = miso_kernel.input3(); + const circle::Tensor *strides = miso_kernel.input4(); + + LUCI_INTERPRETER_CHECK(strides != nullptr); + + const circle::Tensor *output = miso_kernel.output(); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(begin) == DataType::S32); + LUCI_INTERPRETER_CHECK(Tensor::element_type(end) == DataType::S32); + LUCI_INTERPRETER_CHECK(Tensor::element_type(strides) == DataType::S32); + LUCI_INTERPRETER_CHECK(Tensor::element_type(input) == Tensor::element_type(output)); +} + +void execute_kernel_CircleStridedSlice(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) +{ + kernels::MISOKernel miso_kernel(cur_op, runtime_graph); + + const circle::Tensor *input = miso_kernel.input1(); + const circle::Tensor *begin = miso_kernel.input2(); + const circle::Tensor *end = miso_kernel.input3(); + const circle::Tensor *strides = miso_kernel.input4(); + const circle::Tensor *output = miso_kernel.output(); + + const int32_t dims = Tensor::num_dims(input); + + const uint8_t *input_data = runtime_graph->getDataByTensor(input); + const int32_t *begin_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(begin)); + const int32_t *end_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(end)); + const int32_t *strides_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(strides)); + uint8_t *output_data = runtime_graph->getDataByTensor(output); - switch (input()->element_type()) + LUCI_INTERPRETER_CHECK(input_data != nullptr); + LUCI_INTERPRETER_CHECK(begin_data != nullptr); + LUCI_INTERPRETER_CHECK(end_data != nullptr); + LUCI_INTERPRETER_CHECK(strides_data != nullptr); + LUCI_INTERPRETER_CHECK(output_data != nullptr); + + const auto *options = cur_op->builtin_options_as_StridedSliceOptions(); + + auto op_params = buildStridedSliceParams(dims, begin_data, end_data, strides_data, options); + + switch (Tensor::element_type(input)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::StridedSlice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); break; +#endif // DIS_FLOAT +#ifndef DIS_QUANT case DataType::U8: - tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::StridedSlice(op_params, kernels::getTensorShape(input), input_data, + output_data); + break; + case DataType::S8: + luci_interpreter_pal::StridedSlice(op_params, kernels::getTensorShape(input), input_data, + output_data); break; +#endif case DataType::S32: - tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::StridedSlice(op_params, kernels::getTensorShape(input), + kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); break; default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/StridedSlice.h b/onert-micro/luci-interpreter/src/kernels/StridedSlice.h deleted file mode 100644 index d8e8e4f..0000000 --- a/onert-micro/luci-interpreter/src/kernels/StridedSlice.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H -#define LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class StridedSlice : public KernelWithParams -{ -public: - StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, const Tensor *strides, - Tensor *output, const StridedSliceParams ¶ms); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *begin() const { return _inputs[1]; } - const Tensor *end() const { return _inputs[2]; } - const Tensor *strides() const { return _inputs[3]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H diff --git a/onert-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp b/onert-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp index 44da107..3aa2285 100644 --- a/onert-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/StridedSlice.test.cpp @@ -1,6 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,99 +14,62 @@ * limitations under the License. */ -#include "kernels/StridedSlice.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/strided_slice/StridedSliceKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -TEST(StridedSliceTest, Float) +class StridedSliceTest : public ::testing::Test { - std::unique_ptr memory_manager = std::make_unique(); + // Do nothing +}; - Shape input_shape{2, 3, 2}; - std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - Shape begin_shape{3}; - std::vector begin_data{0, 0, 0}; - Shape end_shape{3}; - std::vector end_data{1, 3, 2}; - Shape strides_shape{3}; - std::vector strides_data{1, 1, 1}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor begin_tensor = - makeInputTensor(begin_shape, begin_data, memory_manager.get()); - Tensor end_tensor = makeInputTensor(end_shape, end_data, memory_manager.get()); - Tensor strides_tensor = - makeInputTensor(strides_shape, strides_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); +template +std::vector checkStridedSliceKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - StridedSliceParams params{}; - params.begin_mask = 0; - params.end_mask = 0; - params.ellipsis_mask = 0; - params.new_axis_mask = 0; - params.shrink_axis_mask = 1; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - StridedSlice kernel(&input_tensor, &begin_tensor, &end_tensor, &strides_tensor, &output_tensor, - params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - std::vector output_shape{3, 2}; - std::vector output_data{1, 2, 3, 4, 5, 6}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); -} + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -TEST(StridedSliceTest, Uint8) -{ - std::unique_ptr memory_manager = std::make_unique(); + runtime_module.execute(); - Shape input_shape{2, 3, 2}; - std::vector input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - Shape begin_shape{3}; - std::vector begin_data{0, 0, 0}; - Shape end_shape{3}; - std::vector end_data{1, 3, 2}; - Shape strides_shape{3}; - std::vector strides_data{1, 1, 1}; - Tensor input_tensor = - makeInputTensor(input_shape, 1.0f, 0, input_data, memory_manager.get()); - Tensor begin_tensor = - makeInputTensor(begin_shape, begin_data, memory_manager.get()); - Tensor end_tensor = makeInputTensor(end_shape, end_data, memory_manager.get()); - Tensor strides_tensor = - makeInputTensor(strides_shape, strides_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0f, 0); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - StridedSliceParams params{}; - params.begin_mask = 0; - params.end_mask = 0; - params.ellipsis_mask = 0; - params.new_axis_mask = 0; - params.shrink_axis_mask = 1; - - StridedSlice kernel(&input_tensor, &begin_tensor, &end_tensor, &strides_tensor, &output_tensor, - params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; +} - std::vector output_shape{3, 2}; - std::vector output_data{1, 2, 3, 4, 5, 6}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(output_data)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +TEST_F(StridedSliceTest, MainTest_P) +{ + test_kernel::TestDataStridedSliceKernel test_data_kernel; + std::vector output_data_vector = checkStridedSliceKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } +// TODO: add negative tests? + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Sub.cpp b/onert-micro/luci-interpreter/src/kernels/Sub.cpp index 7b02c1e..5eaed32 100644 --- a/onert-micro/luci-interpreter/src/kernels/Sub.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Sub.cpp @@ -14,150 +14,147 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "kernels/Sub.h" +#include "Builders.h" #include "kernels/Utils.h" -#include "PALSub.h" +#include "kernels/BinaryOpCommon.h" -#include +#include "PALSub.h" namespace luci_interpreter { -namespace kernels -{ -Sub::Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams ¶ms) - : KernelWithParams({input1, input2}, {output}, params) +void configure_kernel_CircleSub(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + kernels::TISOKernel kernel(cur_op, runtime_graph); + + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input1()) == + Tensor::element_type(kernel.input2())); +#ifndef DIS_QUANT + if (Tensor::element_type(kernel.input1()) == DataType::S16) + { + LUCI_INTERPRETER_CHECK(Tensor::zero_points(kernel.input1()).size() == 1 && + Tensor::zero_points(kernel.input2()).size() == 1); + LUCI_INTERPRETER_CHECK(Tensor::zero_point(kernel.input1()) == 0 && + Tensor::zero_point(kernel.input2()) == 0 && + Tensor::zero_point(kernel.output()) == 0); + } +#endif // DIS_QUANT } -void Sub::configure() +void execute_kernel_CircleSub(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(!(input1()->element_type() != input2()->element_type())) - LUCI_INTERPRETER_CHECK(!(input1()->element_type() != output()->element_type())) - // TODO: enable it only if kernel with dynamic shapes - output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); -} + kernels::TISOKernel kernel(cur_op, runtime_graph); -void Sub::execute() const -{ - switch (input1()->element_type()) + const auto *options = cur_op->builtin_options_as_SubOptions(); + + luci_interpreter::RuntimeShape input_shape1 = + kernels::getTensorRuntimeShape(kernel.input1(), runtime_graph); + luci_interpreter::RuntimeShape input_shape2 = + kernels::getTensorRuntimeShape(kernel.input2(), runtime_graph); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input1())) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(); - break; + { + auto tiso_func = luci_interpreter_pal::Sub; + + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastSub4DSlow; + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +#endif // DIS_FLOAT case DataType::S64: - evalInteger(); - break; + { + auto tiso_func = luci_interpreter_pal::Sub; + + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastSub4DSlow; + + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; case DataType::S32: - evalInteger(); - break; + { + auto tiso_func = luci_interpreter_pal::Sub; + + auto broadcast_tiso_func = luci_interpreter_pal::BroadcastSub4DSlow; + + if (is_inplace) + { + kernels::evalTISOInplaceKernel(tiso_func, broadcast_tiso_func, &kernel, options, + std::move(input_shape1), std::move(input_shape2)); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOKernel(tiso_func, broadcast_tiso_func, &kernel, &kernel_data, + options, std::move(input_shape1), std::move(input_shape2)); + } + } + break; +// TODO: fix it +#if 0 +#ifndef DIS_QUANT case DataType::U8: - evalQuantized(); - break; + { + auto tiso_func = [](const tflite::ArithmeticParams ¶ms, + const tflite::RuntimeShape &input1_shape, const uint8_t *input1_data, + const tflite::RuntimeShape &input2_shape, const uint8_t *input2_data, + const tflite::RuntimeShape &output_shape, uint8_t *output_data) { + tflite::reference_ops::Sub(params, input1_shape, input1_data, input2_shape, input2_data, + output_shape, output_data); + }; + auto broadcast_tiso_func = + [](const tflite::ArithmeticParams ¶ms, const tflite::RuntimeShape &input1_shape, + const uint8_t *input1_data, const tflite::RuntimeShape &input2_shape, + const uint8_t *input2_data, const tflite::RuntimeShape &output_shape, + uint8_t *output_data) { + tflite::reference_ops::BroadcastSubSlow(params, input1_shape, input1_data, input2_shape, + input2_data, output_shape, output_data); + }; + if (is_inplace) + { + kernels::evalTISOInplaceQuantizedKernel(tiso_func, broadcast_tiso_func, &kernel, + options); + } + else + { + kernels::TISOData kernel_data = kernel.readData(); + kernels::evalTISOQuantizedKernel(tiso_func, broadcast_tiso_func, &kernel, + &kernel_data, options); + } + } + break; +#endif // DIS_QUANT +#endif // 0 default: assert(false && "Unsupported type."); } } -void Sub::evalFloat() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - tflite::reference_ops::BroadcastSubSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - luci_interpreter_pal::Sub(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -template void Sub::evalInteger() const -{ - tflite::ArithmeticParams params{}; - fillArithmeticActivationRange(params, _params.activation); - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - tflite::reference_ops::BroadcastSubSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -void Sub::evalQuantized() const -{ - const auto input1_scale = static_cast(input1()->scale()); - const auto input2_scale = static_cast(input2()->scale()); - const auto output_scale = static_cast(output()->scale()); - - const int left_shift = 20; - const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale); - const double real_input1_multiplier = input1_scale / twice_max_input_scale; - const double real_input2_multiplier = input2_scale / twice_max_input_scale; - const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale); - - int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{}; - int input1_shift{}, input2_shift{}, output_shift{}; - quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift); - quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift); - quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift); - - int32_t activation_min{}; - int32_t activation_max{}; - calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); - - tflite::ArithmeticParams params{}; - params.left_shift = left_shift; - // The kernel expects inputs' zero points to be negated. - params.input1_offset = -input1()->zero_point(); // Note the '-'. - params.input1_multiplier = input1_multiplier; - params.input1_shift = input1_shift; - params.input2_offset = -input2()->zero_point(); // Note the '-'. - params.input2_multiplier = input2_multiplier; - params.input2_shift = input2_shift; - params.output_offset = output()->zero_point(); - params.output_multiplier = output_multiplier; - params.output_shift = output_shift; - params.quantized_activation_min = activation_min; - params.quantized_activation_max = activation_max; - - const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); - - if (need_broadcast) - { - tflite::reference_ops::BroadcastSubSlow( - params, getTensorShape(input1()), getTensorData(input1()), getTensorShape(input2()), - getTensorData(input2()), getTensorShape(output()), getTensorData(output())); - } - else - { - tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData(input1()), - getTensorShape(input2()), getTensorData(input2()), - getTensorShape(output()), getTensorData(output())); - } -} - -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Sub.h b/onert-micro/luci-interpreter/src/kernels/Sub.h deleted file mode 100644 index f9cedff..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Sub.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_SUB_H -#define LUCI_INTERPRETER_KERNELS_SUB_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Sub : public KernelWithParams -{ -public: - Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams ¶ms); - - const Tensor *input1() const { return _inputs[0]; } - const Tensor *input2() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - template void evalInteger() const; - void evalQuantized() const; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_SUB_H diff --git a/onert-micro/luci-interpreter/src/kernels/Sub.test.cpp b/onert-micro/luci-interpreter/src/kernels/Sub.test.cpp index 9abafd4..ff267b1 100644 --- a/onert-micro/luci-interpreter/src/kernels/Sub.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Sub.test.cpp @@ -15,252 +15,138 @@ * limitations under the License. */ -#include "kernels/Sub.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/sub/FloatSubKernel.h" +#include "luci_interpreter/test_models/sub/IntSubKernel.h" +#include "luci_interpreter/test_models/sub/NegSubKernel.h" -#include +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -using std::pair; -using std::vector; -using std::transform; -using std::initializer_list; class SubTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -// for quantized Add, the error shouldn't exceed step -float GetTolerance(float min, float max) -{ - float kQuantizedStep = (max - min) / 255.0; - return kQuantizedStep; -} - -TEST_F(SubTest, Uint8) +template std::vector checkSubKernel(test_kernel::TestDataBase *test_data_base) { - Shape base_shape = {2, 3, 1, 2}; - vector base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - vector test_shapes = {{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - vector test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - vector> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; - vector> output_data = { - {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f, - 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, - -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f}, - {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f}, - {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, - 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, - -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f}, - {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - float kQuantizedTolerance = GetTolerance(-3.f, 3.f); - pair quant_param = quantizationParams(-3.f, 3.f); - for (size_t i = 0; i < output_data.size(); ++i) - { - Tensor input1_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(getElementType(), quant_param.first, quant_param.second); - - SubParams params{}; - params.activation = Activation::NONE; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 2); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data[i], kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + // set left input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); } - // 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) + // set right input data { - Tensor input1_tensor = makeInputTensor( - test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor( - base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(getElementType(), quant_param.first, quant_param.second); + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(1)); + std::copy(test_data_base->get_input_data_by_index(1).begin(), + test_data_base->get_input_data_by_index(1).end(), input_tensor_data); + } - SubParams params{}; - params.activation = Activation::NONE; + runtime_module.execute(); - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - EXPECT_THAT(dequantizeTensorData(output_tensor), - FloatArrayNear(output_data[i], kQuantizedTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); - } + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(SubTest, Float) +TEST_F(SubTest, Float_P) { - Shape base_shape = {2, 3, 1, 2}; - vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - vector> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; - vector> test_outputs = { - {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f, - 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, - 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}}; - - vector input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, - 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; - vector input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // No broadcast { - Tensor input1_tensor = - makeInputTensor(base_shape, input1_data, _memory_manager.get()); - Tensor input2_tensor = - makeInputTensor(test_shapes[i], input2_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - SubParams params{}; - params.activation = Activation::RELU; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; - - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); + const bool is_with_broadcast = false; + test_kernel::TestDataFloatSub test_data_float_kernel(is_with_broadcast); + std::vector output_data_vector = checkSubKernel(&test_data_float_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_float_kernel.get_output_data_by_index(0), 0.0001f)); } -} - -template void CheckInteger(luci_interpreter::IMemoryManager *memory_manager) -{ - using dtype = typename loco::DataTypeImpl::Type; - Shape base_shape = {2, 3, 1, 2}; - std::vector test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; - std::vector> test_outputs = { - {0, 1, 2, 3, 0, 0, 0, 0, 4, 1, 0, 0, 0, 0, 7, 0, 3, 0, - 0, 2, 4, 4, 0, 0, 3, 0, 10, 0, 6, 0, 3, 0, 10, 2, 6, 0}, - {0, 1, 4, 1, 3, 0, 0, 2, 10, 0, 6, 0}, - {0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 4, 3, 0, 0, 3, 0, 7, 0, - 2, 4, 0, 2, 0, 0, 8, 0, 6, 0, 1, 0, 8, 2, 6, 0, 1, 0}, - {0, 0, 0, 0, 7, 0, 2, 4, 6, 0, 1, 0}}; - std::vector input1_data{-1, 2, 1, 0, 4, -5, 1, 3, 7, -1, 7, 1}; - std::vector input2_data{4, 1, -3, -1, 1, 6}; - for (size_t i = 0; i < test_shapes.size(); ++i) + // With broadcast { - Tensor input1_tensor = makeInputTensor(base_shape, input1_data, memory_manager); - Tensor input2_tensor = makeInputTensor(test_shapes[i], input2_data, memory_manager); - Tensor output_tensor = makeOutputTensor(DType); - - SubParams params{}; - params.activation = Activation::RELU; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - EXPECT_THAT(extractTensorData(output_tensor), test_outputs[i]) - << "With shape number " << i; + const bool is_with_broadcast = true; + test_kernel::TestDataFloatSub test_data_float_kernel(is_with_broadcast); + std::vector output_data_vector = checkSubKernel(&test_data_float_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_float_kernel.get_output_data_by_index(0), 0.0001f)); } -}; - -TEST_F(SubTest, SInt32) -{ - CheckInteger(_memory_manager.get()); - SUCCEED(); } -TEST_F(SubTest, SInt64) +TEST_F(SubTest, INT_P) { - CheckInteger(_memory_manager.get()); - SUCCEED(); + // No broadcast + { + const bool is_with_broadcast = false; + test_kernel::TestDataIntSub test_data_kernel(is_with_broadcast); + const auto output_data_vector = checkSubKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); + } + // With broadcast + { + const bool is_with_broadcast = true; + test_kernel::TestDataIntSub test_data_kernel(is_with_broadcast); + const auto output_data_vector = checkSubKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); + } } -TEST_F(SubTest, Input_Output_Type_NEG) +TEST_F(SubTest, Inputs_type_mismatch_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1.f}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - SubParams params{}; - params.activation = Activation::RELU; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputsTypeMismatchSubKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(SubTest, Invalid_Output_Type_NEG) +TEST_F(SubTest, Input_output_type_mismatch_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - SubParams params{}; - params.activation = Activation::RELU; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchSubKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(SubTest, Invalid_Input_Type_NEG) +TEST_F(SubTest, No_quant_params_NEG) { - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U64); - - SubParams params{}; - params.activation = Activation::RELU; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::NegTestDataNoQuantParamsSubKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } -TEST_F(SubTest, Mismatching_Input_Int_Types_NEG) -{ - Tensor input1_tensor = makeInputTensor({1}, {1}, _memory_manager.get()); - Tensor input2_tensor = makeInputTensor({1}, {2}, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::S32); - - SubParams params{}; - params.activation = Activation::NONE; - - Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add tests for U8 and S16 +// TODO: add tests for inplace optimizations for all types } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/TISOKernel.h b/onert-micro/luci-interpreter/src/kernels/TISOKernel.h new file mode 100644 index 0000000..48dec74 --- /dev/null +++ b/onert-micro/luci-interpreter/src/kernels/TISOKernel.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_TISO_KERNEL_H +#define LUCI_INTERPRETER_KERNELS_TISO_KERNEL_H + +#include "Builders.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +struct TISOData +{ + uint8_t *input1_data = nullptr; + uint8_t *input2_data = nullptr; + uint8_t *output_data = nullptr; +}; + +// Two input single output kernel +class TISOKernel +{ +public: + TISOKernel() = delete; + + explicit TISOKernel(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) + : _runtime_graph(runtime_graph) + { + const auto input1_index = cur_op->inputs()->operator[](0); + const auto input2_index = cur_op->inputs()->operator[](1); + const auto output_index = cur_op->outputs()->operator[](0); + + assert(input1_index != -1); + assert(input2_index != -1); + assert(output_index != -1); + + _input1_tensor = _runtime_graph->getCircleTensorByIndex(input1_index); + _input2_tensor = _runtime_graph->getCircleTensorByIndex(input2_index); + _output_tensor = _runtime_graph->getCircleTensorByIndex(output_index); + + assert(_input1_tensor != nullptr); + assert(_input2_tensor != nullptr); + assert(_output_tensor != nullptr); + } + + const circle::Tensor *input1() const { return _input1_tensor; } + const circle::Tensor *input2() const { return _input2_tensor; } + const circle::Tensor *output() const { return _output_tensor; } + + BaseRuntimeGraph *runtime_graph() const { return _runtime_graph; } + + TISOData readData() + { + auto *input1_data = _runtime_graph->getDataByTensor(_input1_tensor); + if (input1_data == nullptr) + input1_data = _runtime_graph->getConstDataByTensor(_input1_tensor); + assert(input1_data); + + auto *input2_data = _runtime_graph->getDataByTensor(_input2_tensor); + if (input2_data == nullptr) + input2_data = _runtime_graph->getConstDataByTensor(_input2_tensor); + assert(input2_data); + + auto *output_data = _runtime_graph->getDataByTensor(_output_tensor); + assert(output_data); + + return {input1_data, input2_data, output_data}; + } + + TISOData readInplaceData(uint8_t *&inplace_data_ptr, circle::Tensor *&input_inplace_tensor) + { + auto *input1_data = _runtime_graph->getDataByTensor(_input1_tensor); + if (input1_data != nullptr) + { + inplace_data_ptr = const_cast(input1_data); + input_inplace_tensor = const_cast(_input1_tensor); + } + if (input1_data == nullptr) + input1_data = _runtime_graph->getConstDataByTensor(_input1_tensor); + + assert(input1_data); + + auto *input2_data = _runtime_graph->getDataByTensor(_input2_tensor); + if (inplace_data_ptr == nullptr) + { + assert(input2_data != nullptr); + inplace_data_ptr = const_cast(input2_data); + input_inplace_tensor = const_cast(_input2_tensor); + } + if (input2_data == nullptr) + input2_data = _runtime_graph->getConstDataByTensor(_input2_tensor); + assert(input2_data); + + assert(_runtime_graph->getDataByTensor(_output_tensor) == nullptr); + + auto *output_data = inplace_data_ptr; + assert(output_data); + + return {input1_data, input2_data, output_data}; + } + +private: + const circle::Tensor *_input1_tensor; + const circle::Tensor *_input2_tensor; + const circle::Tensor *_output_tensor; + + BaseRuntimeGraph *_runtime_graph; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_TISO_KERNEL_H diff --git a/onert-micro/luci-interpreter/src/kernels/Tanh.cpp b/onert-micro/luci-interpreter/src/kernels/Tanh.cpp index 3acb22e..809a914 100644 --- a/onert-micro/luci-interpreter/src/kernels/Tanh.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Tanh.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,81 +14,191 @@ * limitations under the License. */ -#include "kernels/Tanh.h" - +#include "Builders.h" #include "kernels/Utils.h" +#include "SISOKernel.h" -#include +#include "PALTanh.h" namespace luci_interpreter { -namespace kernels -{ -Tanh::Tanh(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} +#ifndef DIS_QUANT -void Tanh::configure() +namespace +{ +void calculateArithmeticData(const circle::Tensor *input, const circle::Tensor *output, + int32_t &input_zero_point, int32_t &input_range_radius, + int32_t &input_multiplier, int &input_left_shift) { - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); - if (input()->element_type() == DataType::U8) + const auto input_dtype = Tensor::element_type(input); + switch (input_dtype) { - populateLookupTable(); + // TODO: enable it +#if 0 + case DataType::S8: + { + static constexpr int input_integer_bits = 4; + const double input_real_multiplier = static_cast(Tensor::scale(input)) * + static_cast(1 << (31 - input_integer_bits)); + + const double q = std::frexp(input_real_multiplier, &input_left_shift); + input_multiplier = static_cast(std::round(q * (1ll << 31))); + input_range_radius = kernels::calculateInputRadius(input_integer_bits, input_left_shift, 31); + } + break; +#endif + case DataType::S16: + { + static constexpr int input_integer_bits = 3; + static constexpr int output_fractional_bits = 15; + + // These operators are implemented in fixed-point arithmetic, + // which intrinsically wants symmetric ranges (zero_point==0) + // and power-of-two scales (power-of-two is abbreviated below as POT). + // While more general support would be possible by means of rescaling, + // that would add some overhead and some loss of accuracy and wouldn't + // be used at the moment as current quantized LSTM applications are + // happy with symmetric, power-of-two-scales quantization. So we just + // implement that narrow case only for now. + + int input_scale_log2_rounded; + bool param_scale_pot = kernels::checkedLog2(Tensor::scale(input), &input_scale_log2_rounded); + + input_left_shift = (15 - input_integer_bits) + input_scale_log2_rounded; + param_scale_pot &= (input_left_shift == 0 || input_left_shift == 1); + + if (param_scale_pot) + { + input_multiplier = 0; + } + else + { + // Calculate multiplier to change input scale to 1/(3*4096) + // as required by the table lookup. + // The number 3.0 in the multiplier comes from here, + // because the interval is [-10.7, 10.7] instead of [-8, 8]. + // So, in this scaling +/-2^17 represents +/-10.7. + + double multiplier = static_cast(Tensor::scale(input)) * 4096.0 * 3.0; + input_left_shift = 0; + + while (multiplier <= 32767.0 / 2.0 && input_left_shift <= 30) + { + input_left_shift++; + multiplier = multiplier * 2.0; + } + + input_multiplier = static_cast(multiplier); + } + + int output_scale_log2_rounded; + kernels::checkedLog2(Tensor::scale(output), &output_scale_log2_rounded); + assert(output_scale_log2_rounded == -output_fractional_bits); + } + break; + default: + assert(false && "Unsupported type"); } - // TODO: enable it only if kernel with dynamic shapes - output()->resize(input()->shape()); } -void Tanh::execute() const +} // namespace + +void evalInteger(const circle::Tensor *input, const circle::Tensor *output, + BaseRuntimeGraph *runtime_graph) { - switch (input()->element_type()) + int32_t input_zero_point = 0; + int32_t input_range_radius = 0; + int32_t input_multiplier = 0; + int input_left_shift = 0; + + calculateArithmeticData(input, output, input_zero_point, input_range_radius, input_multiplier, + input_left_shift); + + const auto *input_data = runtime_graph->getDataByTensor(input); + assert(input_data); + + auto *output_data = runtime_graph->getDataByTensor(output); + assert(output_data); + + const int flat_size = kernels::getTensorRuntimeShape(input, runtime_graph).flatSize(); + + const auto input_dtype = Tensor::element_type(input); + switch (input_dtype) { - case DataType::FLOAT32: - evalFloat(); + // TODO: enable it +#if 0 + case DataType::S8: + luci_interpreter_pal::Tanh( + input_zero_point, input_range_radius, input_multiplier, input_left_shift, + flat_size, kernels::getTensorData(input_data), kernels::getTensorData(output_data)); break; - case DataType::U8: - evalQuantized(); +#endif // 0 + case DataType::S16: + luci_interpreter_pal::Tanh(input_multiplier, input_left_shift, flat_size, + kernels::getTensorData(input_data), + kernels::getTensorData(output_data)); break; default: - assert(false && "Unsupported type."); + assert(false && "Not support yet"); } } +#endif // DIS_QUANT -void Tanh::evalFloat() const +void configure_kernel_CircleTanh(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - tflite::reference_ops::Tanh(getTensorShape(input()), getTensorData(input()), - getTensorShape(output()), getTensorData(output())); -} + kernels::SISOKernel kernel(cur_op, runtime_graph); -void Tanh::evalQuantized() const -{ - const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output())); - uint8_t *output_data = getTensorData(output()); - const uint8_t *input_data = getTensorData(input()); - for (int i = 0; i < size; ++i) - { - output_data[i] = getTableValue(input_data[i]); - } + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) == + Tensor::element_type(kernel.output())); } -void Tanh::populateLookupTable() +void execute_kernel_CircleTanh(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - const auto input_scale = static_cast(input()->scale()); - const auto input_zero_point = static_cast(input()->zero_point()); - const auto output_scale = static_cast(output()->scale()); - const auto output_zero_point = static_cast(output()->zero_point()); - const float inverse_scale = 1 / output_scale; - int32_t maxval = std::numeric_limits::max(); - int32_t minval = std::numeric_limits::min(); - for (int32_t val = minval; val <= maxval; ++val) + kernels::SISOKernel kernel(cur_op, runtime_graph); + + const auto *input_data = runtime_graph->getDataByTensor(kernel.input()); + assert(input_data); + + auto *output_data = runtime_graph->getDataByTensor(kernel.output()); + + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + + switch (Tensor::element_type(kernel.input())) { - const float dequantized = input_scale * (val - input_zero_point); - const float transformed = std::tanh(dequantized); - const float rescaled = std::round(transformed * inverse_scale); - const int32_t quantized = static_cast(rescaled + output_zero_point); - setTableValue(static_cast(std::max(std::min(maxval, quantized), minval)), - static_cast(val)); +#ifndef DIS_FLOAT + case DataType::FLOAT32: + { + const float *input_data_float = kernels::getTensorData(input_data); + float *output_data_float = kernels::getTensorData(output_data); + if (is_inplace) + { + output_data_float = const_cast(input_data_float); + } + + assert(output_data_float); + + const int flat_size = + kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize(); + + luci_interpreter_pal::Tanh(flat_size, input_data_float, output_data_float); + break; + } +#endif // DIS_FLOAT +#ifndef DIS_QUANT + case DataType::S16: + // TODO: enable it +#if 0 + case DataType::S8: +#endif + evalInteger(kernel.input(), kernel.output(), runtime_graph); + break; +#endif // DIS_QUANT + default: + assert(false && "Unsupported type"); } -} -} // namespace kernels + if (is_inplace) + runtime_graph->makeInplaceOperation(kernel.input(), kernel.output()); +} } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Tanh.h b/onert-micro/luci-interpreter/src/kernels/Tanh.h deleted file mode 100644 index af87bf3..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Tanh.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_TANH_H -#define LUCI_INTERPRETER_KERNELS_TANH_H - -#include "core/Kernel.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Tanh : public Kernel -{ -public: - Tanh(const Tensor *input, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - void evalFloat() const; - void evalQuantized() const; - void populateLookupTable(); - void setTableValue(uint8_t value, uint8_t idx) { _table[idx] = value; }; - uint8_t getTableValue(uint8_t idx) const { return _table[idx]; }; - -private: - uint8_t _table[256]{}; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_TANH_H diff --git a/onert-micro/luci-interpreter/src/kernels/Tanh.test.cpp b/onert-micro/luci-interpreter/src/kernels/Tanh.test.cpp index bfae479..ff55a95 100644 --- a/onert-micro/luci-interpreter/src/kernels/Tanh.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Tanh.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/Tanh.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/tanh/FloatTanhKernel.h" +#include "luci_interpreter/test_models/tanh/NegTanhKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,135 +30,60 @@ using namespace testing; class TanhTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(TanhTest, Float) +template std::vector checkTanhKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Tanh kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 0, -0.9999877, 0.9640275, 0.999329, // - 0.99505475, -0.9640275, 1, 0.7615941, // - }; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); -} + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; -TEST_F(TanhTest, Uint8) -{ - float kMin = -1; - float kMax = 127.f / 128.f; - float kTanhTolerance = 2 * (1. / 256); - std::pair input_quant_param = quantizationParams(8 * kMin, 8 * kMax); - std::pair output_quant_param = quantizationParams(kMin, kMax); - std::vector input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - }; - Tensor input_tensor = - makeInputTensor({2, 6, 4, 1}, input_quant_param.first, input_quant_param.second, - input_data, _memory_manager.get()); - Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); - - Tanh kernel(&input_tensor, &output_tensor); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{ - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - }; - std::vector ref_output_shape{2, 6, 4, 1}; - EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data, kTanhTolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); + + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); + + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } + + runtime_module.execute(); + + assert(main_runtime_graph->getNumOfOutputTensors() == 1); + + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(TanhTest, InputTypeInvalid_NEG) +TEST_F(TanhTest, Float_P) { - std::vector input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - }; - Tensor input_tensor = - makeInputTensor({2, 6, 4, 1}, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Tanh kernel(&input_tensor, &output_tensor); - _memory_manager->allocate_memory(output_tensor); - EXPECT_ANY_THROW(kernel.execute()); + test_kernel::TestDataFloatTanh test_data_kernel; + std::vector output_data_vector = checkTanhKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(TanhTest, InputOutputMismatch_NEG) +TEST_F(TanhTest, Input_output_type_mismatch_NEG) { - std::vector input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - }; - Tensor input_tensor = - makeInputTensor({2, 6, 4, 1}, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - Tanh kernel(&input_tensor, &output_tensor); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchTanhKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } +// TODO: add S16 test + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/TestUtils.cpp b/onert-micro/luci-interpreter/src/kernels/TestUtils.cpp index 78caa37..7e4bf7d 100644 --- a/onert-micro/luci-interpreter/src/kernels/TestUtils.cpp +++ b/onert-micro/luci-interpreter/src/kernels/TestUtils.cpp @@ -27,78 +27,6 @@ namespace testing using ::testing::FloatNear; using ::testing::Matcher; -Tensor makeOutputTensor(DataType element_type) { return Tensor(element_type, {}, {}, ""); } - -Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point) -{ - return Tensor(element_type, {}, {{scale}, {zero_point}}, ""); -} - -std::vector dequantizeTensorData(const Tensor &tensor) -{ - if (tensor.element_type() == DataType::U8) - { - std::vector data = extractTensorData(tensor); - return dequantize(data.data(), data.size(), tensor.scale(), tensor.zero_point()); - } - if (tensor.element_type() == DataType::S8) - { - std::vector data = extractTensorData(tensor); - return dequantize(data.data(), data.size(), tensor.scale(), tensor.zero_point()); - } - else if (tensor.element_type() == DataType::S16) - { - // S16 quantization is symmetric, so zero point should be zero. - for (auto zp : tensor.zero_points()) - { - (void)zp; - assert(zp == 0); - } - - std::vector data = extractTensorData(tensor); - if (tensor.scales().size() == 1) - { - return dequantize(data.data(), data.size(), tensor.scale(), 0); - } - - // quantize_dimension breaks shape into two parts: - // inner dimensions that contains continuous data with one quantization type - // outer dimensions that contains other dimensions - const Shape shape = tensor.shape(); - const int32_t quantized_dimension = tensor.quantized_dimension(); - assert(quantized_dimension < shape.num_dims()); - size_t outer_dims_size = 1; - int32_t quant_dim_size = shape.dim(quantized_dimension); - size_t inner_dims_size = 1; - assert(quant_dim_size == tensor.scales().size()); - - for (int i = 0; i < quantized_dimension; ++i) - outer_dims_size *= shape.dim(i); - for (int i = quantized_dimension + 1; i < shape.num_dims(); ++i) - inner_dims_size *= shape.dim(i); - - assert(shape.num_elements() == outer_dims_size * quant_dim_size * inner_dims_size); - - std::vector dequantized_data; - dequantized_data.reserve(shape.num_elements()); - for (size_t outer_it = 0; outer_it < outer_dims_size; ++outer_it) - for (int32_t channel = 0; channel < quant_dim_size; ++channel) - { - float scale = tensor.scales()[channel]; - size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); - std::vector part_dequantized_data = - dequantize(data.data() + offset, inner_dims_size, scale, 0); - dequantized_data.insert(dequantized_data.end(), part_dequantized_data.begin(), - part_dequantized_data.end()); - } - return dequantized_data; - } - else - { - assert(false && "Unsupported type."); - } -} - Matcher> FloatArrayNear(const std::vector &values, float max_abs_error) { std::vector> matchers; @@ -110,17 +38,6 @@ Matcher> FloatArrayNear(const std::vector &values, flo return ElementsAreArray(matchers); } -std::vector extractTensorShape(const Tensor &tensor) -{ - std::vector result; - int dims = tensor.shape().num_dims(); - for (int i = 0; i < dims; i++) - { - result.push_back(tensor.shape().dim(i)); - } - return result; -} - } // namespace testing } // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/TestUtils.h b/onert-micro/luci-interpreter/src/kernels/TestUtils.h index 1f5a0c3..492044c 100644 --- a/onert-micro/luci-interpreter/src/kernels/TestUtils.h +++ b/onert-micro/luci-interpreter/src/kernels/TestUtils.h @@ -19,7 +19,6 @@ #define LUCI_INTERPRETER_KERNELS_TESTUTILS_H #include "luci_interpreter/core/Tensor.h" -#include "luci_interpreter/MemoryManager.h" #include @@ -33,262 +32,10 @@ namespace kernels namespace testing { -template -std::vector quantize(const float *data, size_t num_elements, float scale, int32_t zero_point); - -template -Tensor makeInputTensor(const Shape &shape, const std::vector::Type> &data, - IMemoryManager *memory_manager) -{ - Tensor tensor(DT, shape, {}, ""); - memory_manager->allocate_memory(tensor); - tensor.writeData(data.data(), data.size() * sizeof(typename DataTypeImpl
::Type)); - return tensor; -} - -/** - * @brief Create layer-wise quantized tensor - * @tparam DT base integer data type, for example DataType::U8, DataType::S16, DataType::S64 - * @param shape desired tensor shape - * @param scale scale of quantized number - * @param zero_point zero point of quantized number, should be 0 for signed datatypes - * @param data floating point data for quantization - * @param memory_manager memory manager for allocating memory to tensor - * @return created tensor - */ -template -Tensor makeInputTensor(const Shape &shape, float scale, int32_t zero_point, - const std::vector &data, IMemoryManager *memory_manager) -{ - using NativeT = typename DataTypeImpl
::Type; - Tensor tensor(DT, shape, {{scale}, {zero_point}}, ""); - std::vector quantized_data = - quantize(data.data(), data.size(), scale, zero_point); - memory_manager->allocate_memory(tensor); - tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT)); - return tensor; -} - -/** - * @brief Create channel-wise quantized tensor - * @tparam DT base integer data type, for example DataType::U8, DataType::S16, DataType::S64 - * @param shape desired tensor shape - * @param scales scales of quantized number - * @param zero_points zero points of quantized number, should be 0 for signed datatypes - * @param quantize_dimension dimension to apply quantization along. Usually channels/output channels - * @param data floating point data for quantization - * @param memory_manager memory manager for allocating memory to tensor - * @return created tensor - */ -template -Tensor makeInputTensor(const Shape &shape, const std::vector &scales, - const std::vector &zero_points, int quantized_dimension, - const std::vector &data, IMemoryManager *memory_manager) -{ - using NativeT = typename DataTypeImpl
::Type; - assert(quantized_dimension < shape.num_dims()); - Tensor tensor(DT, shape, {scales, zero_points, quantized_dimension}, ""); - - // quantize_dimension breaks shape into two parts: - // inner dimensions that contains continuous data with one quantization type - // outer dimensions that contains other dimensions - size_t outer_dims_size = 1; - int32_t quant_dim_size = shape.dim(quantized_dimension); - size_t inner_dims_size = 1; - assert(quant_dim_size == scales.size()); - assert(quant_dim_size == zero_points.size()); - - for (int i = 0; i < quantized_dimension; ++i) - outer_dims_size *= shape.dim(i); - for (int i = quantized_dimension + 1; i < shape.num_dims(); ++i) - inner_dims_size *= shape.dim(i); - - assert(shape.num_elements() == outer_dims_size * quant_dim_size * inner_dims_size); - - std::vector quantized_data; - quantized_data.reserve(shape.num_elements()); - for (size_t outer_it = 0; outer_it < outer_dims_size; ++outer_it) - for (int32_t channel = 0; channel < quant_dim_size; ++channel) - { - int32_t zero_point = zero_points[channel]; - float scale = scales[channel]; - size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); - std::vector part_quantized_data = - quantize(data.data() + offset, inner_dims_size, scale, zero_point); - quantized_data.insert(quantized_data.end(), part_quantized_data.begin(), - part_quantized_data.end()); - } - assert(quantized_data.size() == shape.num_elements()); - memory_manager->allocate_memory(tensor); - tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT)); - return tensor; -} - -Tensor makeOutputTensor(DataType element_type); -Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point); - -std::vector extractTensorShape(const Tensor &tensor); - -// Returns the corresponding DataType given the type T. -template constexpr DataType getElementType() -{ - if (std::is_same::value) - return DataType::FLOAT32; - if (std::is_same::value) - return DataType::FLOAT64; - if (std::is_same::value) - return DataType::U8; - if (std::is_same::value) - return DataType::U16; - if (std::is_same::value) - return DataType::U32; - if (std::is_same::value) - return DataType::U64; - if (std::is_same::value) - return DataType::S8; - if (std::is_same::value) - return DataType::S16; - if (std::is_same::value) - return DataType::S32; - if (std::is_same::value) - return DataType::S64; - if (std::is_same::value) - return DataType::BOOL; - return DataType::Unknown; -} - -template std::vector extractTensorData(const Tensor &tensor) -{ - const auto *data_ptr = tensor.data(); - return std::vector(data_ptr, data_ptr + tensor.shape().num_elements()); -} - -std::vector dequantizeTensorData(const Tensor &tensor); - // Array version of `::testing::FloatNear` matcher. ::testing::Matcher> FloatArrayNear(const std::vector &values, float max_abs_error = 1.0e-5f); -template -std::vector quantize(const float *data, size_t num_elements, float scale, int32_t zero_point) -{ - static_assert(std::is_integral::value, "Integral type expected."); - - float q_min{}, q_max{}; - if (std::is_signed::value) - { - q_min = -std::numeric_limits::max(); - q_max = std::numeric_limits::max(); - } - else - { - q_min = 0; - q_max = std::numeric_limits::max(); - } - - std::vector q; - for (size_t i = 0; i < num_elements; ++i) - { - const auto &f = data[i]; - q.push_back(static_cast( - std::max(q_min, std::min(q_max, std::round(zero_point + (f / scale)))))); - } - return q; -} - -template -std::vector dequantize(const T *data, size_t num_elements, float scale, int32_t zero_point) -{ - static_assert(std::is_integral::value, "Integral type expected."); - std::vector f; - for (size_t i = 0; i < num_elements; ++i) - { - const T &q = data[i]; - f.push_back(scale * (q - zero_point)); - } - return f; -} - -// NOTE Returns scale and zero point for _asymmetric_ range (both signed and unsigned). -template std::pair quantizationParams(float f_min, float f_max) -{ - static_assert(std::is_integral::value, "Integral type expected."); - int32_t zero_point = 0; - float scale = 0; - const T qmin = std::numeric_limits::lowest(); - const T qmax = std::numeric_limits::max(); - const float qmin_double = qmin; - const float qmax_double = qmax; - // 0 should always be a representable value. Let's assume that the initial - // min,max range contains 0. - assert(f_max >= 0); - assert(f_min <= 0); - if (f_min == f_max) - { - // Special case where the min,max range is a point. Should be {0}. - assert(f_max == 0); - assert(f_min == 0); - return {scale, zero_point}; - } - - // General case. - // - // First determine the scale. - scale = (f_max - f_min) / (qmax_double - qmin_double); - - // Zero-point computation. - // First the initial floating-point computation. The zero-point can be - // determined from solving an affine equation for any known pair - // (real value, corresponding quantized value). - // We know two such pairs: (rmin, qmin) and (rmax, qmax). - // The arithmetic error on the zero point computed from either pair - // will be roughly machine_epsilon * (sum of absolute values of terms) - // so we want to use the variant that adds the smaller terms. - const float zero_point_from_min = qmin_double - f_min / scale; - const float zero_point_from_max = qmax_double - f_max / scale; - - const float zero_point_from_min_error = std::abs(qmin_double) + std::abs(f_min / scale); - - const float zero_point_from_max_error = std::abs(qmax_double) + std::abs(f_max / scale); - - const float zero_point_double = zero_point_from_min_error < zero_point_from_max_error - ? zero_point_from_min - : zero_point_from_max; - - // Now we need to nudge the zero point to be an integer - // (our zero points are integer, and this is motivated by the requirement - // to be able to represent the real value "0" exactly as a quantized value, - // which is required in multiple places, for example in Im2col with SAME - // padding). - - T nudged_zero_point = 0; - if (zero_point_double < qmin_double) - { - nudged_zero_point = qmin; - } - else if (zero_point_double > qmax_double) - { - nudged_zero_point = qmax; - } - else - { - nudged_zero_point = static_cast(std::round(zero_point_double)); - } - - // The zero point should always be in the range of quantized value, - // // [qmin, qmax]. - assert(qmax >= nudged_zero_point); - assert(qmin <= nudged_zero_point); - zero_point = nudged_zero_point; - // finally, return the values - return {scale, zero_point}; -} - -inline float getTolerance(float min, float max, int quantize_steps) -{ - return ((max - min) / quantize_steps); -} - } // namespace testing } // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Transpose.cpp b/onert-micro/luci-interpreter/src/kernels/Transpose.cpp index 06ea235..a6018c4 100644 --- a/onert-micro/luci-interpreter/src/kernels/Transpose.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Transpose.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. 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. @@ -15,70 +15,71 @@ * limitations under the License. */ -#include "kernels/Transpose.h" - +#include "Builders.h" +#include "TISOKernel.h" #include "kernels/Utils.h" -#include +#include "PALTranspose.h" namespace luci_interpreter { - -namespace kernels +void configure_kernel_CircleTranspose(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { + kernels::TISOKernel kernel(cur_op, runtime_graph); -Transpose::Transpose(const Tensor *input, const Tensor *perm, Tensor *output) - : Kernel({input, perm}, {output}) -{ -} + LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input2()) == DataType::S32); -void Transpose::configure() -{ - // Transpose op only supports 1D-4D input arrays. - int dims = input()->shape().num_dims(); - const int32_t *perm_data = getTensorData(perm()); - - assert(input()->shape().num_dims() <= 4); - assert(input()->element_type() == output()->element_type()); + const int32_t dims = Tensor::num_dims(kernel.input1()); + const int32_t *perm_data = + kernels::getTensorData(runtime_graph->getConstDataByTensor(kernel.input2())); - assert(perm()->shape().num_dims() == 1); - assert(perm()->shape().dim(0) == dims); + // Ensure validity of the permutations tensor as a 1D tensor + LUCI_INTERPRETER_CHECK(Tensor::num_dims(kernel.input2()) == 1); + LUCI_INTERPRETER_CHECK(Tensor::dim(kernel.input2(), 0) == dims); - Shape output_shape(dims); - for (int i = 0; i < dims; i++) - { - assert(perm_data[i] < dims && perm_data[i] >= 0); - output_shape.dim(i) = input()->shape().dim(perm_data[i]); - } - // TODO: enable it only if kernel with dynamic shapes - - output()->resize(output_shape); + for (int idx = 0; idx < dims; ++idx) + LUCI_INTERPRETER_CHECK(perm_data[idx] >= 0 and perm_data[idx] < dims); } -void Transpose::execute() const +void execute_kernel_CircleTranspose(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - tflite::TransposeParams params{}; - const int32_t *perm_data = getTensorData(perm()); - const int32_t size = perm()->shape().dim(0); + kernels::TISOKernel kernel(cur_op, runtime_graph); + + const circle::Tensor *input = kernel.input1(); + const circle::Tensor *perm = kernel.input2(); + const circle::Tensor *output = kernel.output(); + + kernels::TISOData tiso_data = kernel.readData(); + const int32_t *perm_data = kernels::getTensorData(tiso_data.input2_data); + + const int32_t size = Tensor::dim(perm, 0); + luci_interpreter_pal::TransposeParams params; params.perm_count = size; - for (int i = 0; i < size; i++) + for (int i = 0; i < size; ++i) params.perm[i] = perm_data[i]; - switch (input()->element_type()) + + switch (Tensor::element_type(input)) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - tflite::reference_ops::Transpose(params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::Transpose(params, kernels::getTensorShape(input), + kernels::getTensorData(tiso_data.input1_data), + kernels::getTensorShape(output), + kernels::getTensorData(tiso_data.output_data)); break; +#endif // DIS_FLOAT +#ifndef DIS_QUANT case DataType::U8: - tflite::reference_ops::Transpose(params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::Transpose(params, kernels::getTensorShape(input), + kernels::getTensorData(tiso_data.input1_data), + kernels::getTensorShape(output), + kernels::getTensorData(tiso_data.output_data)); break; +#endif // DIS_QUANT default: - assert(false && "Unsupported type."); + assert(false && "Unsupported type"); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Transpose.h b/onert-micro/luci-interpreter/src/kernels/Transpose.h deleted file mode 100644 index 5619c62..0000000 --- a/onert-micro/luci-interpreter/src/kernels/Transpose.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_TRANSPOSE_H -#define LUCI_INTERPRETER_KERNELS_TRANSPOSE_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class Transpose : public Kernel -{ -public: - Transpose(const Tensor *input, const Tensor *perm, Tensor *output); - - const Tensor *input() const { return _inputs[0]; } - const Tensor *perm() const { return _inputs[1]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_TRANSPOSE_H diff --git a/onert-micro/luci-interpreter/src/kernels/Transpose.test.cpp b/onert-micro/luci-interpreter/src/kernels/Transpose.test.cpp index 8cc6993..dda5212 100644 --- a/onert-micro/luci-interpreter/src/kernels/Transpose.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Transpose.test.cpp @@ -1,6 +1,5 @@ /* * 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. @@ -15,102 +14,62 @@ * limitations under the License. */ -#include "kernels/Transpose.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/transpose/TransposeKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; +class TransposeTest : public ::testing::Test +{ + // Do nothing +}; + template -void Check(std::initializer_list input_shape, std::initializer_list perm_shape, - std::initializer_list output_shape, std::initializer_list input_data, - std::initializer_list perm_data, std::initializer_list output_data) +std::vector checkTransposeKernel(test_kernel::TestDataBase *test_data_base) { - std::unique_ptr memory_manager = std::make_unique(); - constexpr DataType element_type = getElementType(); - Tensor input_tensor = - makeInputTensor(input_shape, input_data, memory_manager.get()); - Tensor perm_tensor = makeInputTensor(perm_shape, perm_data, memory_manager.get()); - Tensor output_tensor = makeOutputTensor(element_type); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Transpose kernel(&input_tensor, &perm_tensor, &output_tensor); - kernel.configure(); - memory_manager->allocate_memory(output_tensor); - kernel.execute(); + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - EXPECT_THAT(extractTensorData(output_tensor), ::testing::ElementsAreArray(output_data)); -} + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); -template class TransposeTest : public ::testing::Test -{ -}; + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } -using DataTypes = ::testing::Types; -TYPED_TEST_SUITE(TransposeTest, DataTypes); + runtime_module.execute(); -TYPED_TEST(TransposeTest, Small3D) -{ - Check(/*input_shape=*/{2, 3, 4}, /*perm_shape=*/{3}, /*output_shape=*/{4, 2, 3}, - /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, - /*perm_data=*/{2, 0, 1}, - /*output_data=*/{0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21, - 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23}); -} + assert(main_runtime_graph->getNumOfOutputTensors() == 1); -TYPED_TEST(TransposeTest, Large4D) -{ - Check( - /*input_shape=*/{2, 3, 4, 5}, /*perm_shape=*/{4}, /*output_shape=*/{4, 2, 3, 5}, - /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, - /*perm_data=*/{2, 0, 1, 3}, - /*output_data=*/{0, 1, 2, 3, 4, 20, 21, 22, 23, 24, 40, 41, 42, 43, 44, - 60, 61, 62, 63, 64, 80, 81, 82, 83, 84, 100, 101, 102, 103, 104, - 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 45, 46, 47, 48, 49, - 65, 66, 67, 68, 69, 85, 86, 87, 88, 89, 105, 106, 107, 108, 109, - 10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, - 70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114, - 15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59, - 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119}); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TYPED_TEST(TransposeTest, Large2D) +TEST_F(TransposeTest, MainTest_P) { - Check( - /*input_shape=*/{10, 12}, /*perm_shape=*/{2}, /*output_shape=*/{12, 10}, - /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, - /*perm_data=*/{1, 0}, - /*output_data=*/{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, - 61, 73, 85, 97, 109, 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, - 3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 4, 16, 28, 40, 52, - 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113, - 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, - 67, 79, 91, 103, 115, 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, - 9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 10, 22, 34, 46, 58, - 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119}); + test_kernel::TestDataTransposeKernel test_data_kernel; + std::vector output_data_vector = checkTransposeKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } +// TODO: add negative tests? + } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/TransposeConv.h b/onert-micro/luci-interpreter/src/kernels/TransposeConv.h index 2a9d2f1..cea0cf3 100644 --- a/onert-micro/luci-interpreter/src/kernels/TransposeConv.h +++ b/onert-micro/luci-interpreter/src/kernels/TransposeConv.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp b/onert-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp index ff98ac0..4856e1b 100644 --- a/onert-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/TransposeConv.test.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.cpp b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.cpp index 57cf9f2..63ef363 100644 --- a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.cpp +++ b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.cpp @@ -19,7 +19,6 @@ #include "kernels/Utils.h" #include "PALUnidirectionalSequenceLSTM.h" -#include "PALApplyActivationToVector.h" namespace luci_interpreter { @@ -47,11 +46,11 @@ bool checkedLog2(const float x, int *log2_result) // are required for input. However, during the hidden state update phase, the // output is the updated hidden state, which is asymmetrically quantized. Thus // output may require zero point -lstm::ArithmeticParams createInterGateParams(const float input1_scale, const float input2_scale, - const float output_scale, const DataType output_type, - const int output_zp) +luci_interpreter_pal::ArithmeticParams +createInterGateParams(const float input1_scale, const float input2_scale, const float output_scale, + const DataType output_type, const int output_zp) { - lstm::ArithmeticParams op_params; + luci_interpreter_pal::ArithmeticParams op_params; if (output_type == DataType::S16) { op_params.quantized_activation_min = std::numeric_limits::min(); @@ -84,7 +83,7 @@ void createGateParams(const circle::Tensor *input, const circle::Tensor *input_w { // Input CalculateOpDataFullyConnected { - lstm::FullyConnectedParams input_gate_params; + luci_interpreter_pal::FullyConnectedParams input_gate_params; double real_multiplier = 0.0; int output_shift; int32_t output_activation_min; @@ -110,7 +109,7 @@ void createGateParams(const circle::Tensor *input, const circle::Tensor *input_w // Recurrent CalculateOpDataFullyConnected { - lstm::FullyConnectedParams recurrent_gate_params; + luci_interpreter_pal::FullyConnectedParams recurrent_gate_params; double real_multiplier = 0.0; int output_shift; int32_t output_activation_min; @@ -246,11 +245,13 @@ void evalInt8(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph, b #endif // DIS_QUANT #ifndef DIS_FLOAT -lstm::FullyConnectedParams createFcParamsFloat() +luci_interpreter_pal::FullyConnectedParams createFcParamsFloat() { - lstm::FullyConnectedParams op_params; + luci_interpreter_pal::FullyConnectedParams op_params; kernels::calculateActivationRange(FusedActFunc::NONE, &op_params.float_activation_min, &op_params.float_activation_max); + op_params.quantized_activation_max = op_params.float_activation_max; + op_params.quantized_activation_min = op_params.float_activation_min; return op_params; } @@ -282,9 +283,11 @@ void prepareGateParamsFloat(lstm::LSTMParameters *float_lstm_params) float_lstm_params->output_gate_parameters = createGateParamsFloat(); // Inter gate multiplication parameters - lstm::ArithmeticParams op_params; + luci_interpreter_pal::ArithmeticParams op_params; kernels::calculateActivationRange(FusedActFunc::NONE, &op_params.float_activation_min, &op_params.float_activation_max); + op_params.quantized_activation_max = op_params.float_activation_max; + op_params.quantized_activation_min = op_params.float_activation_min; float_lstm_params->inter_gate_parameters.forget_cell_mul_params = op_params; float_lstm_params->inter_gate_parameters.input_mul_params = op_params; float_lstm_params->inter_gate_parameters.output_mul_params = op_params; @@ -414,23 +417,25 @@ void configure_kernel_CircleUnidirectionalSequenceLSTM(const circle::Operator *c } void execute_kernel_CircleUnidirectionalSequenceLSTM(const circle::Operator *cur_op, - BaseRuntimeGraph *runtime_graph, bool in_place) + BaseRuntimeGraph *runtime_graph) { const auto input_index = cur_op->inputs()->operator[](0); assert(input_index != -1); + bool is_inplace = runtime_graph->is_inplace_op(cur_op); + const auto input = runtime_graph->getCircleTensorByIndex(input_index); switch (Tensor::element_type(input)) { #ifndef DIS_FLOAT case DataType::FLOAT32: - evalFloat(cur_op, runtime_graph, in_place); + evalFloat(cur_op, runtime_graph, is_inplace); break; #endif // DIS_FLOAT #ifndef DIS_QUANT case DataType::S8: - evalInt8(cur_op, runtime_graph, in_place); + evalInt8(cur_op, runtime_graph, is_inplace); break; #endif // DIS_QUANT default: diff --git a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.h b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.h index 8cdccc1..38c1212 100644 --- a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.h +++ b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2023 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. @@ -18,7 +17,7 @@ #ifndef LUCI_INTERPRETER_KERNELS_UNIDIRECTIONAL_SEQUENCE_LSTM_H #define LUCI_INTERPRETER_KERNELS_UNIDIRECTIONAL_SEQUENCE_LSTM_H -#include "Utils.h" +#include "PALUtils.h" namespace luci_interpreter { @@ -185,43 +184,17 @@ private: const circle::Tensor *internal_tensors[24]; }; -struct FullyConnectedParams -{ - int32_t input_offset; - int32_t weights_offset; - int32_t output_offset; - int32_t output_multiplier; - int32_t output_shift; - int32_t quantized_activation_min; - int32_t quantized_activation_max; - int32_t float_activation_min; - int32_t float_activation_max; -}; - struct GateParameters { - FullyConnectedParams input_fc_params; - FullyConnectedParams recurrent_fc_params; -}; - -struct ArithmeticParams -{ - int32_t input1_offset; - int32_t input2_offset; - int32_t quantized_activation_min; - int32_t quantized_activation_max; - int32_t output_offset; - int32_t output_multiplier; - int32_t output_shift; - int32_t float_activation_min; - int32_t float_activation_max; + luci_interpreter_pal::FullyConnectedParams input_fc_params; + luci_interpreter_pal::FullyConnectedParams recurrent_fc_params; }; struct InterGateParameters { - ArithmeticParams forget_cell_mul_params; - ArithmeticParams input_mul_params; - ArithmeticParams output_mul_params; + luci_interpreter_pal::ArithmeticParams forget_cell_mul_params; + luci_interpreter_pal::ArithmeticParams input_mul_params; + luci_interpreter_pal::ArithmeticParams output_mul_params; }; struct CellStateInfo diff --git a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.test.cpp b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.test.cpp index df059cf..1a094ee 100644 --- a/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/UnidirectionalSequenceLSTM.test.cpp @@ -15,551 +15,71 @@ * limitations under the License. */ -#include "kernels/UnidirectionalSequenceLSTM.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/unidirectional_lstm/FloatUnidirectionalLSTMKernel.h" +#include "luci_interpreter/test_models/unidirectional_lstm/QuantS8UnidirectionalLSTM.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -class UnidirectionalSequenceLSTMTest : public ::testing::Test +class UnidirectionalLSTMTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -// NOTE from NoCifgNoPeepholeNoProjectionNoClippingUnidirectionalLstmTest -TEST_F(UnidirectionalSequenceLSTMTest, FloatTest) +template +std::vector checkUnidirectionalSequenceLSTMKernel(test_kernel::TestDataBase *test_data_base) { - const int32_t n_batch = 1; - const int32_t n_input = 2; - const int32_t n_cell = 4; - const int32_t n_output = 4; - const int32_t sequence_length = 3; - - std::vector input_to_input_weights = {-0.45018822, -0.02338299, -0.0870589, -0.34550029, - 0.04266912, -0.15680569, -0.34856534, 0.43890524}; - - std::vector input_to_cell_weights = {-0.50013041, 0.1370284, 0.11810488, 0.2013163, - -0.20583314, 0.44344562, 0.22077113, -0.29909778}; - - std::vector input_to_forget_weights = {0.09701663, 0.20334584, -0.50592935, -0.31343272, - -0.40032279, 0.44781327, 0.01387155, -0.35593212}; - - std::vector input_to_output_weights = {-0.25065863, -0.28290087, 0.04613829, 0.40525138, - 0.44272184, 0.03897077, -0.1556896, 0.19487578}; - - std::vector input_gate_bias = {0., 0., 0., 0.}; - std::vector forget_gate_bias = {1., 1., 1., 1.}; - std::vector cell_gate_bias = {0., 0., 0., 0.}; - std::vector output_gate_bias = {0., 0., 0., 0.}; - - std::vector recurrent_to_input_weights = { - -0.0063535, -0.2042388, 0.31454784, -0.35746509, 0.28902304, 0.08183324, - -0.16555229, 0.02286911, -0.13566875, 0.03034258, 0.48091322, -0.12528998, - 0.24077177, -0.51332325, -0.33502164, 0.10629296}; - - std::vector recurrent_to_forget_weights = { - -0.48684245, -0.06655136, 0.42224967, 0.2112639, 0.27654213, 0.20864892, - -0.07646349, 0.45877004, 0.00141793, -0.14609534, 0.36447752, 0.09196436, - 0.28053468, 0.01560611, -0.20127171, -0.01140004}; - - std::vector recurrent_to_cell_weights = { - -0.3407414, 0.24443203, -0.2078532, 0.26320225, 0.05695659, -0.00123841, - -0.4744786, -0.35869038, -0.06418842, -0.13502428, -0.501764, 0.22830659, - -0.46367589, 0.26016325, -0.03894562, -0.16368064}; - - std::vector recurrent_to_output_weights = { - 0.43385774, -0.17194885, 0.2718237, 0.09215671, 0.24107647, -0.39835793, - 0.18212086, 0.01301402, 0.48572797, -0.50656658, 0.20047462, -0.20607421, - -0.51818722, -0.15390486, 0.0468148, 0.39922136}; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Shape input_to_input_weights_shape{n_cell, n_input}; - Shape input_to_cell_weights_shape{n_cell, n_input}; - Shape input_to_forget_weights_shape{n_cell, n_input}; - Shape input_to_output_weights_shape{n_cell, n_input}; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - Shape input_gate_bias_shape{n_cell}; - Shape forget_gate_bias_shape{n_cell}; - Shape cell_gate_bias_shape{n_cell}; - Shape output_gate_bias_shape{n_cell}; + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - Shape recurrent_to_input_weights_shape{n_cell, n_output}; - Shape recurrent_to_cell_weights_shape{n_cell, n_output}; - Shape recurrent_to_forget_weights_shape{n_cell, n_output}; - Shape recurrent_to_output_weights_shape{n_cell, n_output}; + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - Tensor input_to_cell_weights_tensor = makeInputTensor( - input_to_cell_weights_shape, input_to_cell_weights, _memory_manager.get()); - Tensor input_to_forget_weights_tensor = makeInputTensor( - input_to_forget_weights_shape, input_to_forget_weights, _memory_manager.get()); - Tensor input_to_output_weights_tensor = makeInputTensor( - input_to_output_weights_shape, input_to_output_weights, _memory_manager.get()); + runtime_module.execute(); - Tensor input_gate_bias_tensor = makeInputTensor( - input_gate_bias_shape, input_gate_bias, _memory_manager.get()); - Tensor forget_gate_bias_tensor = makeInputTensor( - forget_gate_bias_shape, forget_gate_bias, _memory_manager.get()); - Tensor cell_gate_bias_tensor = - makeInputTensor(cell_gate_bias_shape, cell_gate_bias, _memory_manager.get()); - Tensor output_gate_bias_tensor = makeInputTensor( - output_gate_bias_shape, output_gate_bias, _memory_manager.get()); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - Tensor recurrent_to_input_weights_tensor = makeInputTensor( - recurrent_to_input_weights_shape, recurrent_to_input_weights, _memory_manager.get()); - Tensor recurrent_to_cell_weights_tensor = makeInputTensor( - recurrent_to_cell_weights_shape, recurrent_to_cell_weights, _memory_manager.get()); - Tensor recurrent_to_forget_weights_tensor = makeInputTensor( - recurrent_to_forget_weights_shape, recurrent_to_forget_weights, _memory_manager.get()); - Tensor recurrent_to_output_weights_tensor = makeInputTensor( - recurrent_to_output_weights_shape, recurrent_to_output_weights, _memory_manager.get()); - - std::vector input_data{2., 3., 3., 4., 1., 1.}; - Shape input_shape{sequence_length, n_batch, n_input}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - Shape output_state_shape{n_batch, n_output}; - Tensor output_state_tensor = makeOutputTensor(DataType::FLOAT32); - output_state_tensor.resize(output_state_shape); - - Shape cell_state_shape{n_batch, n_cell}; - Tensor cell_state_tensor = makeOutputTensor(DataType::FLOAT32); - cell_state_tensor.resize(cell_state_shape); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 0.0; - params.proj_clip = 0.0; - params.time_major = true; - params.asymmetric_quantize_inputs = false; - - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_forget_weights_tensor, - &input_to_cell_weights_tensor, &input_to_output_weights_tensor, - &recurrent_to_input_weights_tensor, &recurrent_to_forget_weights_tensor, - &recurrent_to_cell_weights_tensor, &recurrent_to_output_weights_tensor, nullptr, nullptr, - nullptr, &input_gate_bias_tensor, &forget_gate_bias_tensor, &cell_gate_bias_tensor, - &output_gate_bias_tensor, nullptr, nullptr, &output_state_tensor, &cell_state_tensor, nullptr, - nullptr, nullptr, nullptr, &output_tensor, &scratchpad_1, &scratchpad_2, &scratchpad_3, params); - - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(output_state_tensor); - _memory_manager->allocate_memory(cell_state_tensor); - _memory_manager->allocate_memory(scratchpad_1); - _memory_manager->allocate_memory(scratchpad_2); - _memory_manager->allocate_memory(scratchpad_3); - kernel.execute(); - - std::vector ref_output_data{-0.02973187, 0.1229473, 0.20885126, -0.15358765, - -0.03716109, 0.12507336, 0.41193449, -0.20860538, - -0.15053082, 0.09120187, 0.24278517, -0.12222792}; - - std::vector ref_output_shape{sequence_length, n_batch, n_output}; - const float tolerance = 1e-5; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, tolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(UnidirectionalSequenceLSTMTest, FloatTest_batch) +TEST_F(UnidirectionalLSTMTest, Float_P) { - const int32_t n_batch = 1; - const int32_t n_input = 2; - const int32_t n_cell = 4; - const int32_t n_output = 4; - const int32_t sequence_length = 3; - - std::vector input_to_input_weights = {-0.45018822, -0.02338299, -0.0870589, -0.34550029, - 0.04266912, -0.15680569, -0.34856534, 0.43890524}; - - std::vector input_to_cell_weights = {-0.50013041, 0.1370284, 0.11810488, 0.2013163, - -0.20583314, 0.44344562, 0.22077113, -0.29909778}; - - std::vector input_to_forget_weights = {0.09701663, 0.20334584, -0.50592935, -0.31343272, - -0.40032279, 0.44781327, 0.01387155, -0.35593212}; - - std::vector input_to_output_weights = {-0.25065863, -0.28290087, 0.04613829, 0.40525138, - 0.44272184, 0.03897077, -0.1556896, 0.19487578}; - - std::vector input_gate_bias = {0., 0., 0., 0.}; - std::vector forget_gate_bias = {1., 1., 1., 1.}; - std::vector cell_gate_bias = {0., 0., 0., 0.}; - std::vector output_gate_bias = {0., 0., 0., 0.}; - - std::vector recurrent_to_input_weights = { - -0.0063535, -0.2042388, 0.31454784, -0.35746509, 0.28902304, 0.08183324, - -0.16555229, 0.02286911, -0.13566875, 0.03034258, 0.48091322, -0.12528998, - 0.24077177, -0.51332325, -0.33502164, 0.10629296}; - - std::vector recurrent_to_forget_weights = { - -0.48684245, -0.06655136, 0.42224967, 0.2112639, 0.27654213, 0.20864892, - -0.07646349, 0.45877004, 0.00141793, -0.14609534, 0.36447752, 0.09196436, - 0.28053468, 0.01560611, -0.20127171, -0.01140004}; - - std::vector recurrent_to_cell_weights = { - -0.3407414, 0.24443203, -0.2078532, 0.26320225, 0.05695659, -0.00123841, - -0.4744786, -0.35869038, -0.06418842, -0.13502428, -0.501764, 0.22830659, - -0.46367589, 0.26016325, -0.03894562, -0.16368064}; - - std::vector recurrent_to_output_weights = { - 0.43385774, -0.17194885, 0.2718237, 0.09215671, 0.24107647, -0.39835793, - 0.18212086, 0.01301402, 0.48572797, -0.50656658, 0.20047462, -0.20607421, - -0.51818722, -0.15390486, 0.0468148, 0.39922136}; - - Shape input_to_input_weights_shape{n_cell, n_input}; - Shape input_to_cell_weights_shape{n_cell, n_input}; - Shape input_to_forget_weights_shape{n_cell, n_input}; - Shape input_to_output_weights_shape{n_cell, n_input}; - - Shape input_gate_bias_shape{n_cell}; - Shape forget_gate_bias_shape{n_cell}; - Shape cell_gate_bias_shape{n_cell}; - Shape output_gate_bias_shape{n_cell}; - - Shape recurrent_to_input_weights_shape{n_cell, n_output}; - Shape recurrent_to_cell_weights_shape{n_cell, n_output}; - Shape recurrent_to_forget_weights_shape{n_cell, n_output}; - Shape recurrent_to_output_weights_shape{n_cell, n_output}; - - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - Tensor input_to_cell_weights_tensor = makeInputTensor( - input_to_cell_weights_shape, input_to_cell_weights, _memory_manager.get()); - Tensor input_to_forget_weights_tensor = makeInputTensor( - input_to_forget_weights_shape, input_to_forget_weights, _memory_manager.get()); - Tensor input_to_output_weights_tensor = makeInputTensor( - input_to_output_weights_shape, input_to_output_weights, _memory_manager.get()); - - Tensor input_gate_bias_tensor = makeInputTensor( - input_gate_bias_shape, input_gate_bias, _memory_manager.get()); - Tensor forget_gate_bias_tensor = makeInputTensor( - forget_gate_bias_shape, forget_gate_bias, _memory_manager.get()); - Tensor cell_gate_bias_tensor = - makeInputTensor(cell_gate_bias_shape, cell_gate_bias, _memory_manager.get()); - Tensor output_gate_bias_tensor = makeInputTensor( - output_gate_bias_shape, output_gate_bias, _memory_manager.get()); - - Tensor recurrent_to_input_weights_tensor = makeInputTensor( - recurrent_to_input_weights_shape, recurrent_to_input_weights, _memory_manager.get()); - Tensor recurrent_to_cell_weights_tensor = makeInputTensor( - recurrent_to_cell_weights_shape, recurrent_to_cell_weights, _memory_manager.get()); - Tensor recurrent_to_forget_weights_tensor = makeInputTensor( - recurrent_to_forget_weights_shape, recurrent_to_forget_weights, _memory_manager.get()); - Tensor recurrent_to_output_weights_tensor = makeInputTensor( - recurrent_to_output_weights_shape, recurrent_to_output_weights, _memory_manager.get()); - - std::vector input_data{2., 3., 3., 4., 1., 1.}; - Shape input_shape{n_batch, sequence_length, n_input}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - Shape output_state_shape{n_batch, n_output}; - Tensor output_state_tensor = makeOutputTensor(DataType::FLOAT32); - output_state_tensor.resize(output_state_shape); - - Shape cell_state_shape{n_batch, n_cell}; - Tensor cell_state_tensor = makeOutputTensor(DataType::FLOAT32); - cell_state_tensor.resize(cell_state_shape); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 0.0; - params.proj_clip = 0.0; - params.time_major = false; - params.asymmetric_quantize_inputs = false; - - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_forget_weights_tensor, - &input_to_cell_weights_tensor, &input_to_output_weights_tensor, - &recurrent_to_input_weights_tensor, &recurrent_to_forget_weights_tensor, - &recurrent_to_cell_weights_tensor, &recurrent_to_output_weights_tensor, nullptr, nullptr, - nullptr, &input_gate_bias_tensor, &forget_gate_bias_tensor, &cell_gate_bias_tensor, - &output_gate_bias_tensor, nullptr, nullptr, &output_state_tensor, &cell_state_tensor, nullptr, - nullptr, nullptr, nullptr, &output_tensor, &output_state_tensor, &cell_state_tensor, - &scratchpad_1, params); - - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(output_state_tensor); - _memory_manager->allocate_memory(cell_state_tensor); - _memory_manager->allocate_memory(scratchpad_1); - kernel.execute(); - - std::vector ref_output_data{-0.02973187, 0.1229473, 0.20885126, -0.15358765, - -0.03716109, 0.12507336, 0.41193449, -0.20860538, - -0.15053082, 0.09120187, 0.24278517, -0.12222792}; - - std::vector ref_output_shape{n_batch, sequence_length, n_output}; - const float tolerance = 1e-5; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, tolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::TestDataFloatUnidirectionalLSTM test_data_kernel; + std::vector output_data_vector = checkUnidirectionalSequenceLSTMKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(UnidirectionalSequenceLSTMTest, FloatTest_simple) +TEST_F(UnidirectionalLSTMTest, Int8_P) { - const int32_t n_batch = 1; - const int32_t n_input = 1; - const int32_t n_cell = 1; - const int32_t n_output = 1; - const int32_t sequence_length = 1; - - std::vector input_to_input_weights = {0.329067}; - std::vector input_to_forget_weights = {0.308059}; - std::vector input_to_cell_weights = {0.152916}; - std::vector input_to_output_weights = {-0.476033}; - - std::vector input_gate_bias = {0.}; - std::vector forget_gate_bias = {1.}; - std::vector cell_gate_bias = {0.}; - std::vector output_gate_bias = {0.}; - - std::vector recurrent_to_input_weights = {0.207806}; - std::vector recurrent_to_forget_weights = {0.028718}; - std::vector recurrent_to_cell_weights = {-0.182756}; - std::vector recurrent_to_output_weights = {-0.960517}; - - Shape input_to_input_weights_shape{n_cell, n_input}; - Shape input_to_cell_weights_shape{n_cell, n_input}; - Shape input_to_forget_weights_shape{n_cell, n_input}; - Shape input_to_output_weights_shape{n_cell, n_input}; - - Shape input_gate_bias_shape{n_cell}; - Shape forget_gate_bias_shape{n_cell}; - Shape cell_gate_bias_shape{n_cell}; - Shape output_gate_bias_shape{n_cell}; - - Shape recurrent_to_input_weights_shape{n_cell, n_output}; - Shape recurrent_to_cell_weights_shape{n_cell, n_output}; - Shape recurrent_to_forget_weights_shape{n_cell, n_output}; - Shape recurrent_to_output_weights_shape{n_cell, n_output}; - - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - Tensor input_to_cell_weights_tensor = makeInputTensor( - input_to_cell_weights_shape, input_to_cell_weights, _memory_manager.get()); - Tensor input_to_forget_weights_tensor = makeInputTensor( - input_to_forget_weights_shape, input_to_forget_weights, _memory_manager.get()); - Tensor input_to_output_weights_tensor = makeInputTensor( - input_to_output_weights_shape, input_to_output_weights, _memory_manager.get()); - - Tensor input_gate_bias_tensor = makeInputTensor( - input_gate_bias_shape, input_gate_bias, _memory_manager.get()); - Tensor forget_gate_bias_tensor = makeInputTensor( - forget_gate_bias_shape, forget_gate_bias, _memory_manager.get()); - Tensor cell_gate_bias_tensor = - makeInputTensor(cell_gate_bias_shape, cell_gate_bias, _memory_manager.get()); - Tensor output_gate_bias_tensor = makeInputTensor( - output_gate_bias_shape, output_gate_bias, _memory_manager.get()); - - Tensor recurrent_to_input_weights_tensor = makeInputTensor( - recurrent_to_input_weights_shape, recurrent_to_input_weights, _memory_manager.get()); - Tensor recurrent_to_cell_weights_tensor = makeInputTensor( - recurrent_to_cell_weights_shape, recurrent_to_cell_weights, _memory_manager.get()); - Tensor recurrent_to_forget_weights_tensor = makeInputTensor( - recurrent_to_forget_weights_shape, recurrent_to_forget_weights, _memory_manager.get()); - Tensor recurrent_to_output_weights_tensor = makeInputTensor( - recurrent_to_output_weights_shape, recurrent_to_output_weights, _memory_manager.get()); - - std::vector input_data{0.03653763}; - Shape input_shape{n_batch, sequence_length, n_input}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - Shape output_state_shape{n_batch, n_output}; - Tensor output_state_tensor = makeOutputTensor(DataType::FLOAT32); - output_state_tensor.resize(output_state_shape); - - Shape cell_state_shape{n_batch, n_cell}; - Tensor cell_state_tensor = makeOutputTensor(DataType::FLOAT32); - cell_state_tensor.resize(cell_state_shape); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 10.0; - params.proj_clip = 0.0; - params.time_major = false; - params.asymmetric_quantize_inputs = false; - - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_forget_weights_tensor, - &input_to_cell_weights_tensor, &input_to_output_weights_tensor, - &recurrent_to_input_weights_tensor, &recurrent_to_forget_weights_tensor, - &recurrent_to_cell_weights_tensor, &recurrent_to_output_weights_tensor, nullptr, nullptr, - nullptr, &input_gate_bias_tensor, &forget_gate_bias_tensor, &cell_gate_bias_tensor, - &output_gate_bias_tensor, nullptr, nullptr, &output_state_tensor, &cell_state_tensor, nullptr, - nullptr, nullptr, nullptr, &output_tensor, &output_state_tensor, &cell_state_tensor, - &scratchpad_1, params); - - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - _memory_manager->allocate_memory(output_state_tensor); - _memory_manager->allocate_memory(cell_state_tensor); - _memory_manager->allocate_memory(scratchpad_1); - kernel.execute(); - - std::vector ref_output_data{0.00139296}; - std::vector ref_output_shape{n_batch, sequence_length, n_output}; - const float tolerance = 1e-5; - auto aa = extractTensorData(output_tensor); - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, tolerance)); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape)); + test_kernel::TestDataInt8UnidirectionalLSTM test_data_kernel; + std::vector output_data_vector = checkUnidirectionalSequenceLSTMKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); } -TEST_F(UnidirectionalSequenceLSTMTest, Unsupported_Type_Configure_NEG) -{ - const int32_t n_batch = 1; - const int32_t n_input = 2; - const int32_t n_cell = 4; - const int32_t n_output = 4; - const int32_t sequence_length = 3; - - std::vector input_data{2, 3, 3, 4, 1, 1}; // int8 is not support as of now - Shape input_shape{sequence_length, n_batch, n_input}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - std::vector input_to_input_weights = {-0.45018822, -0.02338299, -0.0870589, -0.34550029, - 0.04266912, -0.15680569, -0.34856534, 0.43890524}; - Shape input_to_input_weights_shape{n_cell, n_input}; - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 0.0; - params.proj_clip = 0.0; - params.time_major = true; - params.asymmetric_quantize_inputs = false; - - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - nullptr, nullptr, nullptr, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, nullptr, - nullptr, &output_tensor, &scratchpad_1, &scratchpad_2, &scratchpad_3, params); - - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(UnidirectionalSequenceLSTMTest, Invalid_Input_Shape_NEG) -{ - const int32_t n_batch = 1; - const int32_t n_input = 2; - const int32_t n_cell = 4; - const int32_t n_output = 4; - const int32_t sequence_length = 3; - - std::vector input_data{2., 3., 3., 4., 1., 1.}; - Shape input_shape{sequence_length, n_input}; // this is wrong - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - std::vector input_to_input_weights = {-0.45018822, -0.02338299, -0.0870589, -0.34550029, - 0.04266912, -0.15680569, -0.34856534, 0.43890524}; - Shape input_to_input_weights_shape{n_cell, n_input}; - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 0.0; - params.proj_clip = 0.0; - params.time_major = true; - params.asymmetric_quantize_inputs = false; - - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - nullptr, nullptr, nullptr, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, nullptr, - nullptr, &output_tensor, &scratchpad_1, &scratchpad_2, &scratchpad_3, params); - - EXPECT_ANY_THROW(kernel.configure()); -} - -TEST_F(UnidirectionalSequenceLSTMTest, Invalid_Input_Shape_2_NEG) -{ - const int32_t n_batch = 1; - const int32_t n_input = 2; - const int32_t n_cell = 4; - const int32_t n_output = 4; - const int32_t sequence_length = 3; - - std::vector input_data{2., 3., 3., 4., 1., 1.}; - Shape input_shape{sequence_length, n_batch, n_input}; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - - std::vector input_to_input_weights = {-0.45018822, -0.02338299, -0.0870589, -0.34550029, - 0.04266912, -0.15680569, -0.34856534, 0.43890524}; - Shape input_to_input_weights_shape{n_cell, n_input}; - Tensor input_to_input_weights_tensor = makeInputTensor( - input_to_input_weights_shape, input_to_input_weights, _memory_manager.get()); - - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - Tensor scratchpad_1(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_2(DataType::FLOAT32, Shape({}), {}, ""); - Tensor scratchpad_3(DataType::FLOAT32, Shape({}), {}, ""); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = Activation::TANH; - params.cell_clip = 0.0; - params.proj_clip = 0.0; - params.time_major = true; - params.asymmetric_quantize_inputs = false; - - // NOTE provide wrong shaped inputs - UnidirectionalSequenceLSTM kernel( - &input_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - nullptr, nullptr, nullptr, &input_to_input_weights_tensor, &input_to_input_weights_tensor, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, - &input_to_input_weights_tensor, &input_to_input_weights_tensor, nullptr, nullptr, nullptr, - nullptr, &output_tensor, &scratchpad_1, &scratchpad_2, &scratchpad_3, params); - - EXPECT_ANY_THROW(kernel.configure()); -} +// TODO: add negative tests? } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/Unpack.cpp b/onert-micro/luci-interpreter/src/kernels/Unpack.cpp index cc8cf3a..80f4d1f 100644 --- a/onert-micro/luci-interpreter/src/kernels/Unpack.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Unpack.cpp @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Unpack.h b/onert-micro/luci-interpreter/src/kernels/Unpack.h index 4580491..f4a44ec 100644 --- a/onert-micro/luci-interpreter/src/kernels/Unpack.h +++ b/onert-micro/luci-interpreter/src/kernels/Unpack.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/onert-micro/luci-interpreter/src/kernels/Utils.cpp b/onert-micro/luci-interpreter/src/kernels/Utils.cpp index 0810b82..35ab821 100644 --- a/onert-micro/luci-interpreter/src/kernels/Utils.cpp +++ b/onert-micro/luci-interpreter/src/kernels/Utils.cpp @@ -26,6 +26,26 @@ namespace luci_interpreter namespace kernels { +luci_interpreter::RuntimeShape getTensorRuntimeShape(const circle::Tensor *circle_tensor, + BaseRuntimeGraph *runtime_graph) +{ + luci_interpreter::RuntimeShape input_shape = getTensorShape(circle_tensor); + +#ifndef DIS_DYN_SHAPES + auto *dynamic_shape_vector = runtime_graph->getDynamicShapeTensor(circle_tensor); + if (dynamic_shape_vector != nullptr) + { + input_shape.resize(dynamic_shape_vector->dimensionsCount()); + + for (int n = 0; n < dynamic_shape_vector->dimensionsCount(); ++n) + { + input_shape.setDim(n, dynamic_shape_vector->dims(n)); + } + } +#endif // DIS_DYN_SHAPES + return input_shape; +} + template void calculateActivationRange(Activation activation, T *activation_min, T *activation_max) { @@ -74,6 +94,26 @@ template void calculateActivationRange(Activation activation, int64_t *activatio int64_t *activation_max); #ifndef DIS_QUANT +bool checkedLog2(const float x, int *log2_result) +{ + const float x_log2 = std::log(x) * (1.0f / std::log(2.0f)); + const float x_log2_rounded = std::round(x_log2); + const float x_log2_fracpart = x_log2 - x_log2_rounded; + + *log2_result = static_cast(x_log2_rounded); + return std::abs(x_log2_fracpart) < 1e-3f; +} + +int calculateInputRadius(int input_integer_bits, int input_left_shift, int total_signed_bits) +{ + const double max_input_rescaled = 1.0 * ((1 << input_integer_bits) - 1) * + (1LL << (total_signed_bits - input_integer_bits)) / + (1LL << input_left_shift); + // Tighten bound using floor. Suppose that we could use the exact value. + // After scaling the difference, the result would be at the maximum. Thus we + // must ensure that our value has lower magnitude. + return static_cast(std::floor(max_input_rescaled)); +} static void calculateActivationRangeQuantizedImpl(Activation activation, int32_t qmin, int32_t qmax, int32_t zero_point, float scale, @@ -206,13 +246,13 @@ void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quan } #endif -tflite::RuntimeShape calculateShapeForBroadcast(const circle::Tensor *input1, - const circle::Tensor *input2) +luci_interpreter::RuntimeShape calculateShapeForBroadcast(const circle::Tensor *input1, + const circle::Tensor *input2) { const int num_input1_dims = Tensor::num_dims(input1); const int num_input2_dims = Tensor::num_dims(input2); const int num_out_dims = std::max(num_input1_dims, num_input2_dims); - tflite::RuntimeShape output_shape(num_out_dims); + luci_interpreter::RuntimeShape output_shape(num_out_dims); for (int i = 0; i < num_out_dims; ++i) { @@ -225,7 +265,7 @@ tflite::RuntimeShape calculateShapeForBroadcast(const circle::Tensor *input1, bool can_broadcast = input1_dim == 1 || input2_dim == 1; LUCI_INTERPRETER_CHECK(!need_broadcast || can_broadcast); - output_shape.SetDim(num_out_dims - i - 1, std::max(input1_dim, input2_dim)); + output_shape.setDim(num_out_dims - i - 1, std::max(input1_dim, input2_dim)); } return output_shape; diff --git a/onert-micro/luci-interpreter/src/kernels/Utils.h b/onert-micro/luci-interpreter/src/kernels/Utils.h index 8d3cd69..a01d72d 100644 --- a/onert-micro/luci-interpreter/src/kernels/Utils.h +++ b/onert-micro/luci-interpreter/src/kernels/Utils.h @@ -19,11 +19,13 @@ #define LUCI_INTERPRETER_KERNELS_UTILS_H #include "luci_interpreter/core/Tensor.h" - -#include +#include "Builders.h" +#include "Params.h" #include #include +#include + namespace luci_interpreter { namespace kernels @@ -62,8 +64,10 @@ inline int32_t computeOutputSize(Padding padding, int32_t image_size, int32_t fi switch (padding) { case Padding::SAME: + assert(stride != 0); return (image_size + stride - 1) / stride; case Padding::VALID: + assert(stride != 0); return (image_size + stride - effective_filter_size) / stride; default: assert(false); @@ -83,8 +87,8 @@ inline int32_t calcOffset(const circle::Tensor *tensor, int32_t d0, int32_t d1, template void calculateActivationRange(Activation activation, T *activation_min, T *activation_max); -tflite::RuntimeShape calculateShapeForBroadcast(const circle::Tensor *input1, - const circle::Tensor *input2); +luci_interpreter::RuntimeShape calculateShapeForBroadcast(const circle::Tensor *input1, + const circle::Tensor *input2); // Helper wrapper to hide broadcast logic template class BroadcastableWrapper @@ -99,29 +103,71 @@ private: int _stride; }; -inline tflite::RuntimeShape getTensorShape(const circle::Tensor *tensor) +inline luci_interpreter::RuntimeShape getTensorShape(const circle::Tensor *tensor) { if (tensor == nullptr) - return tflite::RuntimeShape(); + return luci_interpreter::RuntimeShape(); + + auto const tensor_shape = Tensor::tensor_shape(tensor); - tflite::RuntimeShape runtime_shape(Tensor::num_dims(tensor)); - for (int i = 0; i < Tensor::num_dims(tensor); ++i) + luci_interpreter::RuntimeShape runtime_shape(tensor_shape.size()); + for (int i = 0; i < tensor_shape.size(); ++i) { - runtime_shape.SetDim(i, Tensor::dim(tensor, i)); + runtime_shape.setDim(i, tensor_shape[i]); } return runtime_shape; } +inline void getTensorDims(const circle::Tensor *tensor, BaseRuntimeGraph *runtime_graph, + int32_t *dims) +{ + if (tensor == nullptr) + { + dims = nullptr; + return; + } + +#ifndef DIS_DYN_SHAPES + auto *dynamic_shape_vector = runtime_graph->getDynamicShapeTensor(tensor); + if (dynamic_shape_vector != nullptr) + { + for (int n = 0; n < dynamic_shape_vector->dimensionsCount(); ++n) + { + dims[n] = dynamic_shape_vector->dims(n); + } + } + else + { + auto const tensor_shape = Tensor::tensor_shape(tensor); + assert(tensor_shape.size() <= kMaxSmallSize); + for (int i = 0; i < tensor_shape.size(); ++i) + { + dims[i] = tensor_shape[i]; + } + } +#else + auto const tensor_shape = Tensor::tensor_shape(tensor); + assert(tensor_shape.size() <= kMaxSmallSize); + for (int i = 0; i < tensor_shape.size(); ++i) + { + dims[i] = tensor_shape[i]; + } +#endif // DIS_DYN_SHAPES +} + template const T *getTensorData(const uint8_t *tensor_data) { return tensor_data != nullptr ? reinterpret_cast(tensor_data) : nullptr; } -template T *getTensorData(uint8_t *tensor_data) +template inline T *getTensorData(uint8_t *tensor_data) { return tensor_data != nullptr ? reinterpret_cast(tensor_data) : nullptr; } +luci_interpreter::RuntimeShape getTensorRuntimeShape(const circle::Tensor *circle_tensor, + BaseRuntimeGraph *runtime_graph); + // A list of tensors in a format that can be used by kernels like split and // concatenation. template class VectorOfTensors @@ -148,7 +194,7 @@ public: // Taking the pointer from inside a std::vector is only OK if the vector is // never modified, so we populate all_shape in the previous loop and then we // are free to grab iterators here. - for (tflite::RuntimeShape &shape : all_shape_) + for (luci_interpreter::RuntimeShape &shape : all_shape_) { all_shape_ptr_.push_back(&shape); } @@ -163,21 +209,14 @@ public: // example: // const RuntimeShape* const* d = v.dims(); // dims[1] are the dimensions of the second tensor in the list. - const tflite::RuntimeShape *const *shapes() const { return all_shape_ptr_.data(); } + const luci_interpreter::RuntimeShape *const *shapes() const { return all_shape_ptr_.data(); } private: std::vector all_data_; - std::vector all_shape_; - std::vector all_shape_ptr_; + std::vector all_shape_; + std::vector all_shape_ptr_; }; -#ifndef DIS_QUANT -void calculateActivationRangeQuantized(Activation activation, const circle::Tensor *output, - int32_t *activation_min, int32_t *activation_max); -void calculateActivationRangeQuantized(Activation activation, int32_t output_zero_point, - float output_scale, DataType data_type, - int32_t *activation_min, int32_t *activation_max); - template constexpr bool one_of_types() { return false; } // Checks if T is equal to one of {U,Other} types @@ -189,27 +228,17 @@ template constexpr bool one_of_types void matrixScalarMultiplyAccumulate(const int8_t *matrix, int32_t scalar, int32_t n_row, int32_t n_col, int32_t *output); -/** - * Fills activation min and max parameters depending on given data type and activation - * - * T is a template parameter, so after optimization this code left with only required if case - * - * @tparam T data type of arithmetic operation output tensor - * @param params tflite params to fill - * @param activation luci_interpreter::Activation of arithmetic operation - */ -template -void fillArithmeticActivationRange(tflite::ArithmeticParams &p, Activation act) -{ - static_assert(one_of_types(), "Unsupported dtype"); +#ifndef DIS_QUANT +bool checkedLog2(const float x, int *log2_result); - if (std::is_same::value) - calculateActivationRange(act, &p.float_activation_min, &p.float_activation_max); - if (std::is_same::value) - calculateActivationRange(act, &p.quantized_activation_min, &p.quantized_activation_max); - else - calculateActivationRange(act, &p.int64_activation_min, &p.int64_activation_max); -} +int calculateInputRadius(int input_integer_bits, int input_left_shift, int total_signed_bits); + +void calculateActivationRangeQuantized(Activation activation, const circle::Tensor *output, + int32_t *activation_min, int32_t *activation_max); + +void calculateActivationRangeQuantized(Activation activation, int32_t output_zero_point, + float output_scale, DataType data_type, + int32_t *activation_min, int32_t *activation_max); // Decompose a double multiplier into a Q0.31 int32 representation of its // significand, and shift representation of its exponent. diff --git a/onert-micro/luci-interpreter/src/kernels/While.cpp b/onert-micro/luci-interpreter/src/kernels/While.cpp index 153bd1a..6826c2b 100644 --- a/onert-micro/luci-interpreter/src/kernels/While.cpp +++ b/onert-micro/luci-interpreter/src/kernels/While.cpp @@ -15,102 +15,170 @@ * limitations under the License. */ -#include "kernels/While.h" +#include "Builders.h" #include "kernels/Utils.h" #include namespace luci_interpreter { -namespace kernels -{ -namespace +void configure_kernel_CircleWhile(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { + auto *main_runtime_graph = runtime_graph; + + auto *runtime_module = runtime_graph->getRuntimeModule(); + + const auto *options = cur_op->builtin_options_as_WhileOptions(); + const auto body_subgraph_index = options->body_subgraph_index(); + const auto cond_subgraph_index = options->cond_subgraph_index(); + + auto *cond_runtime_graph = runtime_module->getRuntimeGraphAt(cond_subgraph_index); + auto *body_runtime_graph = runtime_module->getRuntimeGraphAt(body_subgraph_index); + + body_runtime_graph->selectOwnSubgraph(); + const auto body_input_size = body_runtime_graph->getNumOfInputTensors(); + const auto body_output_size = body_runtime_graph->getNumOfOutputTensors(); + LUCI_INTERPRETER_CHECK(body_input_size == cur_op->inputs()->size()); + LUCI_INTERPRETER_CHECK(body_output_size == cur_op->outputs()->size()); + LUCI_INTERPRETER_CHECK(body_output_size == cur_op->inputs()->size()); + body_runtime_graph->invalidate(); + body_runtime_graph->configure(false); + + cond_runtime_graph->selectOwnSubgraph(); + const auto cond_input_size = cond_runtime_graph->getNumOfInputTensors(); + const auto cond_output_size = cond_runtime_graph->getNumOfOutputTensors(); + LUCI_INTERPRETER_CHECK(cond_input_size == cur_op->inputs()->size()); + LUCI_INTERPRETER_CHECK(cond_output_size == 1); + const circle::Tensor *cond_output_tensor = cond_runtime_graph->getOutputTensorByIndex(0); + LUCI_INTERPRETER_CHECK(Tensor::element_type(cond_output_tensor) == DataType::BOOL); + cond_runtime_graph->invalidate(); + cond_runtime_graph->configure(false); + + main_runtime_graph->selectOwnSubgraph(); +} -void copy(const std::vector &src, const std::vector &dst) +void execute_kernel_CircleWhile(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - for (size_t i = 0; i < src.size(); ++i) - { - LUCI_INTERPRETER_CHECK(dst[i]->element_type() == src[i]->element_type()); - dst[i]->resize(src[i]->shape()); + auto *main_runtime_graph = runtime_graph; + auto *runtime_module = runtime_graph->getRuntimeModule(); - const int32_t num_elements = src[i]->shape().num_elements(); - const std::size_t element_size = getDataTypeSize(src[i]->element_type()); - std::memcpy(dst[i]->data(), src[i]->data(), num_elements * element_size); - } -} + const auto input_size = cur_op->inputs()->size(); -void copy(const std::vector &src, const std::vector &dst) -{ - std::vector const_src; - for (const auto &t : src) - const_src.push_back(t); - copy(const_src, dst); -} + std::vector operation_inputs_data(input_size); + std::vector operation_outputs_data; -// TODO: Think about how allocate memory for output in main graph -void configureTensorsAllocations(const std::vector &tensors, RuntimeGraph *run_graph) -{ - for (auto tensor : tensors) - run_graph->configureAllocations(tensor); -} + std::vector input_sizes(input_size); -} // namespace + bool is_inplace = runtime_graph->is_inplace_op(cur_op); -While::While(std::vector inputs, std::vector outputs, - RuntimeGraph *cond_graph, RuntimeGraph *body_graph) - : Kernel(std::move(inputs), std::move(outputs)), _cond_graph(cond_graph), _body_graph(body_graph) -{ -} + for (int32_t i = 0; i < input_size; ++i) + { + const auto op_input_index = cur_op->inputs()->operator[](i); + const auto op_output_index = cur_op->outputs()->operator[](i); + assert(op_input_index != -1); + assert(op_output_index != -1); + const auto input = main_runtime_graph->getCircleTensorByIndex(op_input_index); + const auto output = main_runtime_graph->getCircleTensorByIndex(op_output_index); + + input_sizes[i] = Tensor::num_elements(input) * size(Tensor::element_type(input)); + + auto *input_data = main_runtime_graph->getDataByTensor(input); + + uint8_t *tensor_data = nullptr; + if (is_inplace) + { + if (input_data == nullptr) + { + tensor_data = new uint8_t[input_sizes[i]]; + input_data = main_runtime_graph->getConstDataByTensor(input); + assert(input_data != nullptr); + std::memcpy(tensor_data, input_data, input_sizes[i]); + } + else + { + tensor_data = input_data; + } + } + else + { + if (input_data == nullptr) + input_data = main_runtime_graph->getConstDataByTensor(input); + assert(input_data != nullptr); + tensor_data = main_runtime_graph->getDataByTensor(output); + assert(tensor_data != nullptr); + std::memcpy(tensor_data, input_data, input_sizes[i]); + } + assert(tensor_data != nullptr); + + operation_inputs_data[i] = tensor_data; + } -void While::configure() -{ - LUCI_INTERPRETER_CHECK(_body_graph->getInputTensors().size() == getInputTensors().size()); - LUCI_INTERPRETER_CHECK(_body_graph->getOutputTensors().size() == getOutputTensors().size()); - LUCI_INTERPRETER_CHECK(_body_graph->getOutputTensors().size() == getInputTensors().size()); + const auto *options = cur_op->builtin_options_as_WhileOptions(); + const auto body_subgraph_index = options->body_subgraph_index(); + const auto cond_subgraph_index = options->cond_subgraph_index(); - LUCI_INTERPRETER_CHECK(_cond_graph->getInputTensors().size() == getInputTensors().size()); + auto *cond_runtime_graph = runtime_module->getRuntimeGraphAt(cond_subgraph_index); + auto *body_runtime_graph = runtime_module->getRuntimeGraphAt(body_subgraph_index); - const auto &cond_outputs = _cond_graph->getOutputTensors(); - LUCI_INTERPRETER_CHECK(cond_outputs.size() == 1) - LUCI_INTERPRETER_CHECK(cond_outputs[0]->element_type() == DataType::BOOL); -} + do + { + cond_runtime_graph->selectOwnSubgraph(); -/** - * @note Dynamic shape such as {1, 0, 8} may fail in tensor->data() - */ -void While::execute() const -{ - const auto &cond_inputs = _cond_graph->getInputTensors(); - const auto &cond_outputs = _cond_graph->getOutputTensors(); + for (int32_t i = 0; i < input_size; ++i) + cond_runtime_graph->configureGraphInput(i, operation_inputs_data[i]); - configureTensorsAllocations(cond_inputs, _cond_graph); + cond_runtime_graph->execute(); - copy(getInputTensors(), cond_inputs); + bool cond_value = (cond_runtime_graph->getOutputDataByIndex(0))[0]; + if (!cond_value) + break; - const auto &body_inputs = _body_graph->getInputTensors(); - const auto &body_outputs = _body_graph->getOutputTensors(); + body_runtime_graph->selectOwnSubgraph(); + for (int32_t i = 0; i < input_size; ++i) + body_runtime_graph->configureGraphInput(i, operation_inputs_data[i]); - configureTensorsAllocations(body_inputs, _body_graph); + body_runtime_graph->execute(); - while (true) - { - _cond_graph->execute(); + for (int32_t i = 0; i < input_size; ++i) + { + auto cur_output_body_data = body_runtime_graph->getOutputDataByIndex(i); + if (cur_output_body_data == nullptr) + continue; + std::memcpy(operation_inputs_data[i], cur_output_body_data, input_sizes[i]); + } + } while (true); - bool cond_value = cond_outputs[0]->data()[0]; - if (!cond_value) - break; + cond_runtime_graph->resetOutputTensorsData(); + cond_runtime_graph->clearTensors(); - copy(cond_inputs, body_inputs); + body_runtime_graph->selectOwnSubgraph(); + body_runtime_graph->resetOutputTensorsData(); + body_runtime_graph->clearTensors(); - _body_graph->execute(); + main_runtime_graph->selectOwnSubgraph(); - copy(body_outputs, cond_inputs); + if (is_inplace) + { + for (int32_t i = 0; i < input_size; ++i) + { + const auto op_input_index = cur_op->inputs()->operator[](i); + const auto op_output_index = cur_op->outputs()->operator[](i); + assert(op_input_index != -1); + assert(op_output_index != -1); + const auto input = main_runtime_graph->getCircleTensorByIndex(op_input_index); + const auto output = main_runtime_graph->getCircleTensorByIndex(op_output_index); + + if (main_runtime_graph->getDataByTensor(input)) + { + main_runtime_graph->makeInplaceOperation(input, output); + } + else + { + main_runtime_graph->setDataToTensor(output, operation_inputs_data[i]); + } + } } - - copy(cond_inputs, getOutputTensors()); } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/While.h b/onert-micro/luci-interpreter/src/kernels/While.h deleted file mode 100644 index 56f98ba..0000000 --- a/onert-micro/luci-interpreter/src/kernels/While.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * Copyright 2019 The TensorFlow Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LUCI_INTERPRETER_KERNELS_WHILE_H -#define LUCI_INTERPRETER_KERNELS_WHILE_H - -#include "core/Kernel.h" -#include "core/RuntimeGraph.h" - -namespace luci_interpreter -{ -namespace kernels -{ - -class While : public Kernel -{ -public: - While(std::vector inputs, std::vector outputs, RuntimeGraph *cond_graph, - RuntimeGraph *body_graph); - - const Tensor *input(int index) const { return _inputs[index]; } - Tensor *output(int index) const { return _outputs[index]; } - - void configure() override; - void execute() const override; - -private: - RuntimeGraph *const _cond_graph = nullptr; - RuntimeGraph *const _body_graph = nullptr; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_WHILE_H diff --git a/onert-micro/luci-interpreter/src/kernels/While.test.cpp b/onert-micro/luci-interpreter/src/kernels/While.test.cpp index cb8f891..4873b9c 100644 --- a/onert-micro/luci-interpreter/src/kernels/While.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/While.test.cpp @@ -15,87 +15,73 @@ * limitations under the License. */ -#include "core/RuntimeModule.h" -#include "kernels/Add.h" -#include "kernels/Less.h" -#include "kernels/While.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/while/WhileKernel.h" +#include "luci_interpreter/test_models/while/NegWhileKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { using namespace testing; -RuntimeGraph *buildCondSubgraph(RuntimeModule *module, DataType dtype, Tensor *input_cond, - IMemoryManager *memory_manager) +class WhileTest : public ::testing::Test { - RuntimeGraph *graph = module->addGraph(memory_manager); - Tensor *input = - graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); - Tensor *output = - graph->addTensor(std::make_unique(DataType::BOOL, Shape{}, AffineQuantization{}, "")); - - memory_manager->allocate_memory(*input); - memory_manager->allocate_memory(*output); + // Do nothing +}; - graph->setInputTensors({input}); - graph->setOutputTensors({output}); - - graph->addKernel(std::make_unique(input, input_cond, output)); +template std::vector checkWhileKernel(test_kernel::TestDataBase *test_data_base) +{ + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - return graph; -} + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); -RuntimeGraph *buildBodySubgraph(RuntimeModule *module, DataType dtype, Tensor *input_add, - IMemoryManager *memory_manager) -{ - RuntimeGraph *graph = module->addGraph(memory_manager); - Tensor *input = - graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); - Tensor *output = - graph->addTensor(std::make_unique(dtype, Shape{}, AffineQuantization{}, "")); + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); - memory_manager->allocate_memory(*input); - memory_manager->allocate_memory(*output); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(main_runtime_graph->configureGraphInput(0)); + std::copy(test_data_base->get_input_data_by_index(0).begin(), + test_data_base->get_input_data_by_index(0).end(), input_tensor_data); + } - graph->setInputTensors({input}); - graph->setOutputTensors({output}); + runtime_module.execute(); - AddParams params{}; - params.activation = Activation::NONE; - graph->addKernel(std::make_unique(input, input_add, output, params)); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - return graph; + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST(WhileTest, FloatLoop10) +TEST_F(WhileTest, MainTest_P) { - std::unique_ptr memory_manager = std::make_unique(); - Tensor input = makeInputTensor({1}, {1}, memory_manager.get()); - Tensor output = makeOutputTensor(DataType::FLOAT32); - - Tensor input_cond = makeInputTensor({1}, {10}, memory_manager.get()); - Tensor input_add = makeInputTensor({1}, {1}, memory_manager.get()); - - RuntimeModule module(nullptr); - RuntimeGraph *cond_graph = - buildCondSubgraph(&module, DataType::FLOAT32, &input_cond, memory_manager.get()); - RuntimeGraph *body_graph = - buildBodySubgraph(&module, DataType::FLOAT32, &input_add, memory_manager.get()); - - While kernel({&input}, {&output}, cond_graph, body_graph); - kernel.configure(); - memory_manager->allocate_memory(output); - kernel.execute(); + test_kernel::TestDataWhileKernel test_data_kernel; + std::vector output_data_vector = checkWhileKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0)); +} - EXPECT_THAT(extractTensorData(output), FloatArrayNear({10})); +TEST_F(WhileTest, MainTest_NEG) +{ + test_kernel::NegTestDataWhileKernel test_data_kernel; + + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/GraphLoader.cpp b/onert-micro/luci-interpreter/src/loader/GraphLoader.cpp index cb9dd7f..26e207a 100644 --- a/onert-micro/luci-interpreter/src/loader/GraphLoader.cpp +++ b/onert-micro/luci-interpreter/src/loader/GraphLoader.cpp @@ -21,38 +21,62 @@ namespace luci_interpreter namespace { -// TODO: add more operations -bool isCouldBeEmplaceOperation(circle::BuiltinOperator op) +bool isInplaceOperation(const circle::BuiltinOperator &op) { switch (op) { + case circle::BuiltinOperator_ABS: case circle::BuiltinOperator_LOGISTIC: case circle::BuiltinOperator_RESHAPE: + case circle::BuiltinOperator_ELU: case circle::BuiltinOperator_EXPAND_DIMS: + case circle::BuiltinOperator_EXP: + case circle::BuiltinOperator_TANH: + case circle::BuiltinOperator_LEAKY_RELU: + case circle::BuiltinOperator_RELU: + case circle::BuiltinOperator_RELU6: + case circle::BuiltinOperator_ADD: + case circle::BuiltinOperator_MUL: + case circle::BuiltinOperator_SUB: + case circle::BuiltinOperator_WHILE: return true; default: return false; } } -bool isCouldBeEmplaceTensor(CircleReader *reader, const int32_t tensor_index) +bool isSingleUsageOfTensor(CircleReader *reader, const int32_t tensor_index) { uint32_t usage_count = 0; - for (uint32_t i = 0; i < reader->operators().size(); ++i) + + const auto operators = reader->operators(); + for (uint32_t i = 0; i < operators.size(); ++i) { - const auto op = reader->operators().at(i); + const auto *op = operators.at(i); assert(op != nullptr); - for (int32_t j = 0; j < op->inputs()->size(); ++j) + const auto *op_inputs = op->inputs(); + for (int32_t j = 0; j < op_inputs->size(); ++j) { - const auto input_index = op->inputs()->operator[](j); + const auto input_index = op_inputs->operator[](j); if (input_index == tensor_index) - usage_count++; - - if (usage_count > 1) - return false; + { + if (++usage_count > 1) + return false; + } } } + + // Let's check that it is not graph output + if (usage_count == 1) + { + const auto &outputs_indexes = reader->outputs(); + bool is_graph_output = (std::find(outputs_indexes.begin(), outputs_indexes.end(), + tensor_index) != outputs_indexes.end()); + if (is_graph_output) + return false; + } + return true; } @@ -60,29 +84,83 @@ bool isCouldBeEmplaceTensor(CircleReader *reader, const int32_t tensor_index) void GraphLoader::checkInplaceOps(CircleReader *reader, RuntimeGraph *runtime_graph) { - for (uint32_t i = 0; i < reader->operators().size(); ++i) + const auto operators = reader->operators(); + const auto graph_outputs = reader->outputs(); + for (uint32_t i = 0; i < operators.size(); ++i) { - const auto *op = reader->operators().at(i); + const auto *op = operators.at(i); assert(op != nullptr); - bool is_graph_input = false; - for (int32_t j = 0; j < op->inputs()->size(); ++j) + // Check inplace optimization for operation with single input and single output + if (isInplaceOperation(reader->builtin_code(op))) { - const auto input_index = op->inputs()->operator[](j); - if (input_index == -1) - continue; + const auto *op_inputs = op->inputs(); + const auto *op_outputs = op->outputs(); - const auto &inputs_indexes = reader->inputs(); + bool is_inplace = true; + auto non_const_input_it = op_inputs->begin(); + while (true) + { + non_const_input_it = + std::find_if(non_const_input_it, op_inputs->end(), [&reader](const auto input_idx) { + if (input_idx == -1) + return false; - is_graph_input = (std::find(inputs_indexes.begin(), inputs_indexes.end(), input_index) != - inputs_indexes.end()) or - is_graph_input; + return not Tensor::is_constant_tensor(reader, reader->tensors()[input_idx]); + }); - if (not is_graph_input and isCouldBeEmplaceOperation(reader->builtin_code(op)) and - op->outputs()->size() == 1 and isCouldBeEmplaceTensor(reader, input_index)) - { - runtime_graph->addInplaceOpIndex(i); + if (non_const_input_it == op_inputs->end()) + break; + + auto dist = std::distance(op_inputs->begin(), non_const_input_it); + + const auto non_const_input_idx = *non_const_input_it; + + // Check single usage of input tensor + if (not isSingleUsageOfTensor(reader, non_const_input_idx)) + { + is_inplace = false; + break; + } + + // Let's check single usage of output tensor + if (dist >= op_outputs->size() and op_outputs->size() == 1) + dist = 0; + assert(dist < op_outputs->size()); + const auto output_index = op_outputs->operator[](dist); + if (not isSingleUsageOfTensor(reader, output_index)) + { + is_inplace = false; + break; + } + + // Check that num elements are equal + { + const auto *input_non_const_tensor = reader->tensors().at(non_const_input_idx); + const auto *output_tensor = reader->tensors().at(output_index); + if (Tensor::num_elements(input_non_const_tensor) != Tensor::num_elements(output_tensor)) + { + is_inplace = false; + break; + } + } + + // Let's check that output is not a graph output tensor + // TODO: check this statement + { + if (std::find(graph_outputs.begin(), graph_outputs.end(), output_index) != + graph_outputs.end()) + { + is_inplace = false; + break; + } + } + + non_const_input_it++; } + + if (is_inplace) + runtime_graph->addInplaceOpIndex(op); } } } diff --git a/onert-micro/luci-interpreter/src/loader/ModuleLoader.cpp b/onert-micro/luci-interpreter/src/loader/ModuleLoader.cpp index 0bb38c6..dcfa329 100644 --- a/onert-micro/luci-interpreter/src/loader/ModuleLoader.cpp +++ b/onert-micro/luci-interpreter/src/loader/ModuleLoader.cpp @@ -22,7 +22,7 @@ namespace luci_interpreter { void ModuleLoader::load(RuntimeModule *runtime_module, SimpleMemoryManager *memory_manager, - const char *model_data_raw) + const char *model_data_raw, bool dealloc_input) { const circle::Model *model = circle::GetModel(model_data_raw); @@ -32,19 +32,16 @@ void ModuleLoader::load(RuntimeModule *runtime_module, SimpleMemoryManager *memo for (size_t i = 0; i < reader.num_subgraph(); ++i) { + if (!reader.select_subgraph(i)) + assert(false && "Error during select subgraph"); runtime_module->addGraph(memory_manager); - } #ifndef USE_STATIC_ALLOC - for (size_t i = 0; i < reader.num_subgraph(); ++i) - { - if (!reader.select_subgraph(i)) - assert(false && "Error during select subgraph"); auto *runtime_graph = runtime_module->getRuntimeGraphAt(i); // For Dynamic memory manager we can use inplace optimization GraphLoader::checkInplaceOps(&reader, runtime_graph); - } #endif // USE_STATIC_ALLOC + } // For Dynamic Memory manager we build memory allocate/deallocate plan and then configure kernels. // For Static Memory manager we only configure kernels. @@ -54,9 +51,12 @@ void ModuleLoader::load(RuntimeModule *runtime_module, SimpleMemoryManager *memo #ifdef USE_STATIC_ALLOC runtime_graph->configure_kernels(); #else - runtime_graph->configure(); + runtime_graph->configure(dealloc_input); #endif // USE_STATIC_ALLOC } + + // Select main subgraph + reader.select_subgraph(0); } } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/ModuleLoader.h b/onert-micro/luci-interpreter/src/loader/ModuleLoader.h index c35c72d..bfe3e70 100644 --- a/onert-micro/luci-interpreter/src/loader/ModuleLoader.h +++ b/onert-micro/luci-interpreter/src/loader/ModuleLoader.h @@ -29,7 +29,7 @@ class ModuleLoader { public: static void load(RuntimeModule *runtime_module, MemoryManager *memory_manager, - const char *model_data_raw); + const char *model_data_raw, bool dealloc_input); }; } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Add.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Add.cpp deleted file mode 100644 index 2a2140b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Add.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Add.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleAdd(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsAddOptions(); - - AddParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input1, input2, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp b/onert-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp deleted file mode 100644 index 0da72da..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/ArgMax.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/ArgMax.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleArgMax(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *axis = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsArgMaxOptions(); - - ArgMaxParams params{}; - params.output_type = static_cast(options->output_type); - - return std::make_unique(input, axis, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp b/onert-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp deleted file mode 100644 index 9a5af1c..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/AveragePool2D.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/AveragePool2D.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleAveragePool2D(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsPool2DOptions(); - - Pool2DParams params{}; - params.padding = luci_padding(options->padding); - params.filter_height = options->filter_height; - params.filter_width = options->filter_width; - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - params.activation = luci_actfunc(options->fused_activation_function); - - // It is unknown what data will be stored in scratchpad tensor, - // using UINT8 as a most general option - auto scratchpad = std::make_unique(DataType::U8, Shape({}), nullptr); - scratchpad->set_data_buffer(nullptr); - // TODO move tensors offset initialization to one place - // TODO handle with static manager - Tensor *tmp = builder.get_runtime_graph()->addTensor(std::move(scratchpad)); - - return std::make_unique(input, output, tmp, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp b/onert-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp deleted file mode 100644 index 7799331..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/BatchMatMul.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/BatchMatMul.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleBatchMatMul(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *lhs = inputs.at(0); - const Tensor *rhs = inputs.at(1); - Tensor *output = outputs.at(0); - - auto lhs_scratchpad = std::make_unique(lhs->element_type(), Shape({}), nullptr); - lhs_scratchpad->set_data_buffer(nullptr); - auto rhs_scratchpad = std::make_unique(rhs->element_type(), Shape({}), nullptr); - rhs_scratchpad->set_data_buffer(nullptr); - // TODO move tensors offset initialization to one place - // TODO handle with StaticManager - Tensor *lhs_tmp = builder.get_runtime_graph()->addTensor(std::move(lhs_scratchpad)); - Tensor *rhs_tmp = builder.get_runtime_graph()->addTensor(std::move(rhs_scratchpad)); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsBatchMatMulOptions(); - - BatchMatMulParams params; - params.adj_x = options->adjoint_lhs; - params.adj_y = options->adjoint_rhs; - - return std::make_unique(lhs, rhs, output, lhs_tmp, rhs_tmp, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp b/onert-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp deleted file mode 100644 index 424844a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/BatchToSpaceND.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/BatchToSpaceND.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleBatchToSpaceND(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *block_shape = inputs.at(1); - const Tensor *crops = inputs.at(2); - Tensor *output = outputs.at(0); - - return std::make_unique(input, block_shape, crops, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp deleted file mode 100644 index e2b847a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Concatenation.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Concatenation.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleConcatenation(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - std::vector input_tensors(inputs.size()); - for (uint32_t i = 0; i < inputs.size(); ++i) - { - input_tensors[i] = inputs.at(i); - } - Tensor *output = outputs.at(0); - ; - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsConcatenationOptions(); - - ConcatenationParams params{}; - params.axis = options->axis; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(std::move(input_tensors), output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp deleted file mode 100644 index 1750e8a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Conv2D.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Conv2D.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleConv2D(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *filter = inputs.at(1); - const Tensor *bias = inputs.at(2); - Tensor *output = outputs.at(0); - - // It is unknown what data will be stored in scratchpad tensor, - // using UINT8 as a most general option - auto scratchpad = std::make_unique(DataType::U8, Shape({}), nullptr); - scratchpad->set_data_buffer(nullptr); - // TODO move tensors offset initialization to one place - // TODO handle with StaticManager - Tensor *tmp = builder.get_runtime_graph()->addTensor(std::move(scratchpad)); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsConv2DOptions(); - - Conv2DParams params{}; - params.padding = luci_padding(options->padding); - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - params.dilation_height_factor = options->dilation_h_factor; - params.dilation_width_factor = options->dilation_w_factor; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input, filter, bias, output, tmp, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp b/onert-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp deleted file mode 100644 index ebab0cc..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/DepthToSpace.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/DepthToSpace.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleDepthToSpace(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsDepthToSpaceOptions(); - - DepthToSpaceParams params{}; - params.block_size = options->block_size; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp b/onert-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp deleted file mode 100644 index cebad55..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/DepthwiseConv2D.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/DepthwiseConv2D.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleDepthwiseConv2D(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *filter = inputs.at(1); - const Tensor *bias = inputs.at(2); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsDepthwiseConv2DOptions(); - - DepthwiseConv2DParams params{}; - params.padding = luci_padding(options->padding); - params.depth_multiplier = options->depth_multiplier; - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - params.dilation_height_factor = options->dilation_h_factor; - params.dilation_width_factor = options->dilation_w_factor; - params.activation = luci_actfunc(options->fused_activation_function); - - // It is unknown what data will be stored in scratchpad tensor, - // using UINT8 as a most general option - auto scratchpad = std::make_unique(DataType::U8, Shape({}), nullptr); - scratchpad->set_data_buffer(nullptr); - // TODO move tensors offset initialization to one place - // TODO handle with StaticManager - Tensor *tmp = builder.get_runtime_graph()->addTensor(std::move(scratchpad)); - - return std::make_unique(input, filter, bias, output, tmp, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp deleted file mode 100644 index 06b7518..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Dequantize.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Dequantize.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleDequantize(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Div.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Div.cpp deleted file mode 100644 index 3331df9..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Div.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Div.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleDiv(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsDivOptions(); - - DivParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input1, input2, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Elu.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Elu.cpp deleted file mode 100644 index 2d89328..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Elu.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Elu.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleElu(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Equal.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Equal.cpp deleted file mode 100644 index eee5009..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Equal.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Equal.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleEqual(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) - -{ - assert(inputs.size() == 2); - - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp b/onert-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp deleted file mode 100644 index f4745b3..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/ExpandDims.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/ExpandDims.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleExpandDims(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *input = inputs.at(0); - const Tensor *axis = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input, axis, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Fill.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Fill.cpp deleted file mode 100644 index cd5a336..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Fill.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Fill.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleFill(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *dims = inputs.at(0); - const Tensor *value = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(dims, value, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp b/onert-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp deleted file mode 100644 index 7caf123..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/FloorDiv.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/FloorDiv.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleFloorDiv(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp b/onert-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp deleted file mode 100644 index cb35c0f..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/FullyConnected.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/FullyConnected.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleFullyConnected(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *weights = inputs.at(1); - const Tensor *bias = inputs.at(2); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsFullyConnectedOptions(); - - FullyConnectedParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - params.keep_num_dims = options->keep_num_dims; - - return std::make_unique(input, weights, bias, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Gather.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Gather.cpp deleted file mode 100644 index 02cb7e6..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Gather.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Gather.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleGather(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *params = inputs.at(0); - const Tensor *indices = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsGatherOptions(); - - GatherParams gparams{}; - gparams.axis = options->axis; - // TODO support batch_dims - gparams.batch_dims = 0; - - return std::make_unique(params, indices, output, gparams); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Greater.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Greater.cpp deleted file mode 100644 index 160911e..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Greater.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Greater.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleGreater(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp b/onert-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp deleted file mode 100644 index 4c19682..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/GreaterEqual.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/GreaterEqual.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleGreaterEqual(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp b/onert-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp deleted file mode 100644 index 1d7f639..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/InstanceNorm.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/InstanceNorm.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleInstanceNorm(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *gamma = inputs.at(1); - const Tensor *beta = inputs.at(2); - - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsInstanceNormOptions(); - - InstanceNormParams params{}; - params.epsilon = options->epsilon; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input, gamma, beta, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp b/onert-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp deleted file mode 100644 index 6435f09..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/L2Normalize.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/L2Normalize.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleL2Normalize(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsL2NormOptions(); - - L2NormParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp b/onert-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp deleted file mode 100644 index d297525..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/L2Pool2D.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/L2Pool2D.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleL2Pool2D(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsPool2DOptions(); - - Pool2DParams params{}; - params.padding = luci_padding(options->padding); - params.filter_height = options->filter_height; - params.filter_width = options->filter_width; - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp deleted file mode 100644 index 2cb27e2..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LeakyRelu.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LeakyRelu.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLeakyRelu(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsLeakyReluOptions(); - - LeakyReluParams params{}; - params.alpha = options->alpha; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Less.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Less.cpp deleted file mode 100644 index 9de9dbe..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Less.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Less.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLess(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp deleted file mode 100644 index 71ed634..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LocalResponseNormalization.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LocalResponseNormalization.h" - -namespace luci_interpreter -{ - -std::unique_ptr -build_kernel_CircleLocalResponseNormalization(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsLocalResponseNormalizationOptions(); - - LocalResponseNormalizationParams params{}; - params.radius = options->radius; - params.bias = options->bias; - params.alpha = options->alpha; - params.beta = options->beta; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp deleted file mode 100644 index 03e40e9..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LogSoftmax.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LogSoftmax.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLogSoftmax(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp deleted file mode 100644 index 32677a7..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LogicalAnd.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LogicalAnd.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLogicalAnd(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp deleted file mode 100644 index 43ec1e4..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LogicalNot.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LogicalNot.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLogicalNot(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp b/onert-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp deleted file mode 100644 index 7ce29a8..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/LogicalOr.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/LogicalOr.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLogicalOr(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Logistic.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Logistic.cpp deleted file mode 100644 index 113b5ea..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Logistic.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Logistic.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleLogistic(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp b/onert-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp deleted file mode 100644 index 40fff4d..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/MaxPool2D.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/MaxPool2D.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMaxPool2D(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsPool2DOptions(); - - Pool2DParams params{}; - params.padding = luci_padding(options->padding); - params.filter_height = options->filter_height; - params.filter_width = options->filter_width; - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Maximum.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Maximum.cpp deleted file mode 100644 index 1a7930a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Maximum.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Maximum.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMaximum(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Mean.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Mean.cpp deleted file mode 100644 index 6ef48bc..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Mean.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Mean.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMean(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *axis = inputs.at(1); - Tensor *output = outputs.at(0); - - auto temp_index_unique = std::make_unique(DataType::S32, Shape({}), nullptr); - temp_index_unique->set_data_buffer(nullptr); - Tensor *temp_index = builder.get_runtime_graph()->addTensor(std::move(temp_index_unique)); - - auto resolved_axes_unique = std::make_unique(DataType::S32, Shape({}), nullptr); - resolved_axes_unique->set_data_buffer(nullptr); - Tensor *resolved_axes = builder.get_runtime_graph()->addTensor(std::move(resolved_axes_unique)); - - auto temp_sum_unique = std::make_unique(input->element_type(), Shape({}), nullptr); - temp_sum_unique->set_data_buffer(nullptr); - Tensor *temp_sum = builder.get_runtime_graph()->addTensor(std::move(temp_sum_unique)); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsReducerOptions(); - - ReducerParams params{}; - params.keep_dims = options->keep_dims; - - return std::make_unique(input, axis, output, temp_index, resolved_axes, temp_sum, - params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Minimum.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Minimum.cpp deleted file mode 100644 index 232ebf0..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Minimum.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Minimum.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMinimum(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp b/onert-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp deleted file mode 100644 index d96ad25..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/MirrorPad.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/MirrorPad.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMirrorPad(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *paddings = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsMirrorPadOptions(); - - MirrorPadParams params{}; - params.mode = luci_mirrorpad_mode(options->mode); - - return std::make_unique(input, paddings, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Mul.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Mul.cpp deleted file mode 100644 index 283f042..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Mul.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Mul.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleMul(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsMulOptions(); - - MulParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input1, input2, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp b/onert-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp deleted file mode 100644 index 1c84931..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/NotEqual.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/NotEqual.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleNotEqual(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *x = inputs.at(0); - const Tensor *y = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(x, y, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/OneHot.cpp b/onert-micro/luci-interpreter/src/loader/nodes/OneHot.cpp deleted file mode 100644 index a1c8a5b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/OneHot.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/OneHot.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleOneHot(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 4); - - const Tensor *indices = inputs.at(0); - const Tensor *depth = inputs.at(1); - const Tensor *on_value = inputs.at(2); - const Tensor *off_value = inputs.at(3); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsOneHotOptions(); - - OneHotParams params{}; - params.axis = options->axis; - - return std::make_unique(indices, depth, on_value, off_value, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Pack.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Pack.cpp deleted file mode 100644 index a92196b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Pack.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Pack.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CirclePack(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - std::vector input_tensors(inputs.size()); - for (uint32_t i = 0; i < inputs.size(); ++i) - { - input_tensors[i] = inputs.at(i); - } - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsPackOptions(); - - PackParams params{}; - params.axis = options->axis; - params.values_count = options->values_count; - - return std::make_unique(std::move(input_tensors), output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Pad.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Pad.cpp deleted file mode 100644 index 26aa7e7..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Pad.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Pad.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CirclePad(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *paddings = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input, paddings, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/PadV2.cpp b/onert-micro/luci-interpreter/src/loader/nodes/PadV2.cpp deleted file mode 100644 index 829c47f..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/PadV2.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/PadV2.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CirclePadV2(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *paddings = inputs.at(1); - const Tensor *constant_values = inputs.at(2); - Tensor *output = outputs.at(0); - - return std::make_unique(input, paddings, constant_values, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Pow.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Pow.cpp deleted file mode 100644 index 005c281..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Pow.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Pow.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CirclePow(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Quantize.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Quantize.cpp deleted file mode 100644 index 4fa7e66..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Quantize.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Quantize.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleQuantize(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Relu.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Relu.cpp deleted file mode 100644 index 27fcb6b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Relu.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Relu.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleRelu(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Relu6.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Relu6.cpp deleted file mode 100644 index 68dba2c..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Relu6.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Relu6.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleRelu6(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Reshape.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Reshape.cpp deleted file mode 100644 index 45ebf0a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Reshape.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Reshape.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleReshape(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *shape = inputs.at(1); - Tensor *output = outputs.at(0); - - // NOTE 'newShape' attribute is ignored. - return std::make_unique(input, shape, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp b/onert-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp deleted file mode 100644 index bd82048..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/ResizeBilinear.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/ResizeBilinear.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleResizeBilinear(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *size = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsResizeBilinearOptions(); - - ResizeBilinearParams params{}; - params.align_corners = options->align_corners; - params.half_pixel_centers = options->half_pixel_centers; - - return std::make_unique(input, size, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp b/onert-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp deleted file mode 100644 index bc59e87..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/ResizeNearestNeighbor.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/ResizeNearestNeighbor.h" - -namespace luci_interpreter -{ - -std::unique_ptr -build_kernel_CircleResizeNearestNeighbor(std::vector &&inputs, - std::vector &&outputs, const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *size = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsResizeNearestNeighborOptions(); - - ResizeNearestNeighborParams params{}; - params.align_corners = options->align_corners; - // TODO update half_pixel_centers after CircleResizeNearestNeighbor updated - // Current CircleResizeNearestNeighbor don't have half_pixel_centers. - // default value on current is false. - // it need to be updated when CircleResizeNearestNeighbor updated. - params.half_pixel_centers = false; - - return std::make_unique(input, size, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp b/onert-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp deleted file mode 100644 index 0b23ee0..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/ReverseV2.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/ReverseV2.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleReverseV2(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *axis = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input, axis, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp deleted file mode 100644 index 87ca438..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Rsqrt.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Rsqrt.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleRsqrt(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/SVDF.cpp b/onert-micro/luci-interpreter/src/loader/nodes/SVDF.cpp deleted file mode 100644 index 8a77459..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/SVDF.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/SVDF.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSVDF(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 5); - - const Tensor *input = inputs.at(0); - const Tensor *feature = inputs.at(1); - const Tensor *time = inputs.at(2); - const Tensor *bias = inputs.at(3); - const Tensor *input_activation_state = inputs.at(4); - Tensor *output = outputs.at(0); - - auto scratchpad_tensor = - std::make_unique(input_activation_state->element_type(), Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - DataType data_type = input->element_type() == DataType::S8 ? DataType::S32 : DataType::FLOAT32; - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_1 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - if (data_type == DataType::FLOAT32 && - (feature->element_type() == DataType::S8 || feature->element_type() == DataType::U8)) - { - data_type = feature->element_type(); - } - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_2 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - data_type = DataType::FLOAT32; - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_3 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_4 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_5 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - scratchpad_tensor = std::make_unique(data_type, Shape({}), nullptr); - scratchpad_tensor->set_data_buffer(nullptr); - Tensor *tmp_6 = builder.get_runtime_graph()->addTensor(std::move(scratchpad_tensor)); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsSVDFOptions(); - - SVDFParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - params.svdf_rank = options->rank; - params.asymmetric_quantize_inputs = options->asymmetric_quantize_inputs; - - return std::make_unique(input, feature, time, bias, input_activation_state, output, - tmp, tmp_1, tmp_2, tmp_3, tmp_4, tmp_5, tmp_6, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Shape.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Shape.cpp deleted file mode 100644 index 69a727f..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Shape.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Shape.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleShape(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsShapeOptions(); - - ShapeParams shape_params{}; - shape_params.out_type = luci_datatype(options->out_type); - - return std::make_unique(input, output, shape_params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Slice.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Slice.cpp deleted file mode 100644 index e28742b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Slice.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Slice.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSlice(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *begin = inputs.at(1); - const Tensor *size = inputs.at(2); - Tensor *output = outputs.at(0); - - return std::make_unique(input, begin, size, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Softmax.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Softmax.cpp deleted file mode 100644 index 1957a76..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Softmax.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Softmax.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSoftmax(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsSoftmaxOptions(); - - SoftmaxParams params{}; - params.beta = options->beta; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp b/onert-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp deleted file mode 100644 index c5553c6..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/SpaceToBatchND.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/SpaceToBatchND.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSpaceToBatchND(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *block_shape = inputs.at(1); - const Tensor *paddings = inputs.at(2); - Tensor *output = outputs.at(0); - - return std::make_unique(input, block_shape, paddings, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp b/onert-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp deleted file mode 100644 index 156976a..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/SpaceToDepth.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/SpaceToDepth.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSpaceToDepth(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsSpaceToDepthOptions(); - - SpaceToDepthParams params{}; - params.block_size = options->block_size; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Split.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Split.cpp deleted file mode 100644 index ecec1cb..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Split.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Split.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSplit(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *axis = inputs.at(0); - const Tensor *input = inputs.at(1); - std::vector output_tensors(outputs.size()); - - for (uint32_t i = 0; i < outputs.size(); ++i) - { - output_tensors[i] = outputs.at(i).first; - } - - // NOTE 'num_splits' attribute is ignored. - return std::make_unique(axis, input, std::move(output_tensors)); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/SplitV.cpp b/onert-micro/luci-interpreter/src/loader/nodes/SplitV.cpp deleted file mode 100644 index a4c0ae2..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/SplitV.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/SplitV.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSplitV(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 3); - - const Tensor *input = inputs.at(0); - const Tensor *sizes_data = inputs.at(1); - const Tensor *axis = inputs.at(2); - std::vector output_tensors(outputs.size()); - - for (uint32_t i = 0; i < outputs.size(); ++i) - { - output_tensors[i] = outputs.at(i).first; - } - - // NOTE 'num_splits' attribute is ignored. - return std::make_unique(input, sizes_data, axis, std::move(output_tensors)); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp deleted file mode 100644 index 3eaf234..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Sqrt.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Sqrt.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSqrt(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Square.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Square.cpp deleted file mode 100644 index 1afc6cc..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Square.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Square.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSquare(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp b/onert-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp deleted file mode 100644 index 0a5ba78..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/SquaredDifference.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/SquaredDifference.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSquaredDifference(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input1, input2, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp deleted file mode 100644 index 4f0c265..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Squeeze.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Squeeze.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSqueeze(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsSqueezeOptions(); - - SqueezeParams params{}; - params.squeeze_dims = options->squeeze_dims; - - return std::make_unique(input, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp b/onert-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp deleted file mode 100644 index c0a53fc..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/StridedSlice.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/StridedSlice.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleStridedSlice(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 4); - - const Tensor *input = inputs.at(0); - const Tensor *begin = inputs.at(1); - const Tensor *end = inputs.at(2); - const Tensor *strides = inputs.at(3); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsStridedSliceOptions(); - - StridedSliceParams params{}; - params.begin_mask = options->begin_mask; - params.ellipsis_mask = options->ellipsis_mask; - params.end_mask = options->end_mask; - params.new_axis_mask = options->new_axis_mask; - params.shrink_axis_mask = options->shrink_axis_mask; - - return std::make_unique(input, begin, end, strides, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Sub.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Sub.cpp deleted file mode 100644 index 79c773b..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Sub.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Sub.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleSub(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input1 = inputs.at(0); - const Tensor *input2 = inputs.at(1); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsSubOptions(); - - SubParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - - return std::make_unique(input1, input2, output, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Tanh.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Tanh.cpp deleted file mode 100644 index f4aff4c..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Tanh.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Tanh.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleTanh(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - const Tensor *input = inputs.at(0); - Tensor *output = outputs.at(0); - - return std::make_unique(input, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Transpose.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Transpose.cpp deleted file mode 100644 index 83b466d..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Transpose.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Transpose.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleTranspose(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 2); - - const Tensor *input = inputs.at(0); - const Tensor *perm = inputs.at(1); - Tensor *output = outputs.at(0); - - return std::make_unique(input, perm, output); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp b/onert-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp deleted file mode 100644 index 06ee63e..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/TransposeConv.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/TransposeConv.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleTransposeConv(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, - KernelBuilder &builder) -{ - assert(inputs.size() == 4); - - const Tensor *input_sizes = inputs.at(0); - const Tensor *filter = inputs.at(1); - const Tensor *out_backprop = inputs.at(2); - const Tensor *bias = inputs.at(3); - Tensor *output = outputs.at(0); - - DataType scratch_data_type = - input_sizes->element_type() == DataType::S16 ? DataType::S64 : DataType::S32; - - auto scratch_tensor = std::make_unique(scratch_data_type, Shape({}), nullptr); - scratch_tensor->set_data_buffer(nullptr); - Tensor *tmp = builder.get_runtime_graph()->addTensor(std::move(scratch_tensor)); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsTransposeConvOptions(); - - TransposeConvParams params{}; - params.padding = luci_padding(options->padding); - params.stride_height = options->stride_h; - params.stride_width = options->stride_w; - - return std::make_unique(input_sizes, filter, out_backprop, bias, output, - tmp, params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/UnidirectionalSequenceLSTM.cpp b/onert-micro/luci-interpreter/src/loader/nodes/UnidirectionalSequenceLSTM.cpp deleted file mode 100644 index b66e53f..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/UnidirectionalSequenceLSTM.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2023 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 "Builders.h" - -#include "kernels/UnidirectionalSequenceLSTM.h" - -namespace luci_interpreter -{ - -std::unique_ptr -build_kernel_CircleUnidirectionalSequenceLSTM(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 24); - const Tensor *input = inputs.at(0); - const Tensor *input_to_input_weights = inputs.at(1); - const Tensor *input_to_forget_weights = inputs.at(2); - const Tensor *input_to_cell_weights = inputs.at(3); - const Tensor *input_to_output_weights = inputs.at(4); - - const Tensor *recurrent_to_input_weights = inputs.at(5); - const Tensor *recurrent_to_forget_weights = inputs.at(6); - const Tensor *recurrent_to_cell_weights = inputs.at(7); - const Tensor *recurrent_to_output_weights = inputs.at(8); - - const Tensor *cell_to_input_weights = inputs.at(9); - const Tensor *cell_to_forget_weights = inputs.at(10); - const Tensor *cell_to_output_weights = inputs.at(11); - - const Tensor *input_gate_bias = inputs.at(12); - const Tensor *forget_gate_bias = inputs.at(13); - const Tensor *cell_gate_bias = inputs.at(14); - const Tensor *output_gate_bias = inputs.at(15); - - const Tensor *projection_weights = inputs.at(16); - const Tensor *projection_bias = inputs.at(17); - - Tensor *output_state = const_cast(inputs.at(18)); - Tensor *cell_state = const_cast(inputs.at(19)); - - const Tensor *input_layer_norm_coefficients = inputs.at(20); - const Tensor *forget_layer_norm_coefficients = inputs.at(21); - const Tensor *cell_layer_norm_coefficients = inputs.at(22); - const Tensor *output_layer_norm_coefficients = inputs.at(23); - Tensor *output = outputs.at(0); - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsUnidirectionalSequenceLSTMOptions(); - - UnidirectionalSequenceLSTMParams params{}; - params.activation = luci_actfunc(options->fused_activation_function); - params.cell_clip = options->cell_clip; - params.proj_clip = options->proj_clip; - params.time_major = options->time_major; - params.asymmetric_quantize_inputs = options->asymmetric_quantize_inputs; - - // scratch pad tensor - const bool is_integer = input->element_type() == DataType::S8; - bool use_layer_norm = (forget_layer_norm_coefficients != nullptr); - - if (is_integer) - { - if (not use_layer_norm) - { - params.intermediate_affine_quant = - builder.get_runtime_graph()->getIntermediateAffineQuantizations(); - - // For integer LSTM need 4 16-bit buffer with size n_batch * n_cell - // and 1 8-bit buffer with size n_batch * n_cell - auto tmp_1 = std::make_unique(DataType::S16, Shape({}), nullptr); - tmp_1->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(tmp_1))); - - auto tmp_2 = std::make_unique(DataType::S16, Shape({}), nullptr); - tmp_2->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(tmp_2))); - - auto tmp_3 = std::make_unique(DataType::S16, Shape({}), nullptr); - tmp_3->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(tmp_3))); - - auto tmp_4 = std::make_unique(DataType::S16, Shape({}), nullptr); - tmp_4->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(tmp_4))); - - auto tmp_5 = std::make_unique( - DataType::S8, Shape({}), - builder.get_runtime_graph()->getIntermediateAffineQuantizations()[0]); - tmp_5->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(tmp_5))); - } - else - { - // TODO: support float - assert(false && "Not supported now"); - } - } - else - { - // NOTE provide more scratch pads if support hybrid or integer - auto sp_output_state = - std::make_unique(output_state->element_type(), Shape({}), nullptr); - sp_output_state->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(sp_output_state))); - - auto sp_cell_state = std::make_unique(cell_state->element_type(), Shape({}), nullptr); - sp_cell_state->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(sp_cell_state))); - - auto sp_3 = std::make_unique(input->element_type(), Shape({}), nullptr); - sp_3->set_data_buffer(nullptr); - outputs.push_back(builder.get_runtime_graph()->addTensor(std::move(sp_3))); - } - - outputs.push_back(output_state); - outputs.push_back(cell_state); - - return std::make_unique( - input, input_to_input_weights, input_to_forget_weights, input_to_cell_weights, - input_to_output_weights, recurrent_to_input_weights, recurrent_to_forget_weights, - recurrent_to_cell_weights, recurrent_to_output_weights, cell_to_input_weights, - cell_to_forget_weights, cell_to_output_weights, input_gate_bias, forget_gate_bias, - cell_gate_bias, output_gate_bias, projection_weights, projection_bias, - input_layer_norm_coefficients, forget_layer_norm_coefficients, cell_layer_norm_coefficients, - output_layer_norm_coefficients, std::move(outputs), params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/loader/nodes/Unpack.cpp b/onert-micro/luci-interpreter/src/loader/nodes/Unpack.cpp deleted file mode 100644 index 7f067f4..0000000 --- a/onert-micro/luci-interpreter/src/loader/nodes/Unpack.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Builders.h" - -#include "kernels/Unpack.h" - -namespace luci_interpreter -{ - -std::unique_ptr build_kernel_CircleUnpack(std::vector &&inputs, - std::vector &&outputs, - const uint32_t op_index, KernelBuilder &builder) -{ - assert(inputs.size() == 1); - - const Tensor *input = inputs.at(0); - std::vector output_tensors(outputs.size()); - - for (uint32_t i = 0; i < outputs.size(); ++i) - { - output_tensors[i] = outputs.at(i); - } - - circle::OperatorT oper_t; - builder.get_circle_reader()->operators()[op_index]->UnPackTo(&oper_t); - const auto *options = oper_t.builtin_options.AsUnpackOptions(); - - UnpackParams params{}; - params.axis = options->axis; - - // NOTE 'num' attribute is ignored. - return std::make_unique(input, std::move(output_tensors), params); -} - -} // namespace luci_interpreter diff --git a/onert-micro/standalone/CMakeLists.txt b/onert-micro/standalone/CMakeLists.txt index 2dd7ef1..e849597 100644 --- a/onert-micro/standalone/CMakeLists.txt +++ b/onert-micro/standalone/CMakeLists.txt @@ -3,8 +3,12 @@ project(luci_interpreter_micro_standalone) include(${NNAS_ROOT}/infra/onert-micro/utils.cmake) -nnas_find_package(FlatBuffersSource EXACT 2.0 QUIET) -include_directories(${FlatBuffersSource_DIR}/include) +if (NOT ${NOT_BUILD_EXTERNALS}) + nnas_find_package(FlatBuffersSource EXACT 2.0 QUIET) + include_directories(${FlatBuffersSource_DIR}/include) +else() + include_directories(${FlatBuffersSource_DIR}) +endif() # TODO: fix luci/plan for new luci-micro without luci/IR add_subdirectory(${NNAS_PROJECT_SOURCE_DIR}/onert-micro/luci-interpreter ${CMAKE_CURRENT_BINARY_DIR}/luci-interpreter) diff --git a/packaging/ABSEIL.tar.gz b/packaging/ABSEIL.tar.gz index dc7aab548c42af1c8e03e4d431916b192d66ca03..1d92825b5d99e964098d8e23ea5dc5bd0b7173f6 100644 GIT binary patch literal 2151177 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 z]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

#=*C~dip&$tty!WLD^cSTUpQJ7jlcX)&m9(u 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

`??u-_2 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

s9$!S{q&RhVY0F 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

B;)+g#$PL=70&%0gmlVJr?U3}b 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%0D7D|bi4 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%0AffQ`VH~w@g$;R)EVL+{;7}w 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�}Vsa%&cBn)3qcr&NQ3~BNay2 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

1d{fADbD(Y(@*$xL+^Qu?-q0-h`M+z6 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%0D%#3yGqsY7abq8AbYMKYP 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�HwA4 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 US!9t#mPO_N0l5OCJ^%=;0QAQJX#fBK literal 1909045 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

f0I&~)AA}1nm?(%m!Q)r7f#G(X=1Oa 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%0AI{ioSqu}M#e^Dq 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%0D9TmKkU%f6sj>8)uP% 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 z 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

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

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

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

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

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

jn42dEZ=Dz3Nhncm~Pp_)A8vIwMclfGW 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[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 z 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%0DoI|e&>EIo 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[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